/*
1.1 休眠方法
语法: Thread.sleep(毫秒)
用法: 可以用在主线程或子线程中
目的: 在线程中可以复现互抢资源的现象
*/
//1.2 线程优先级设置
//线程优先级设置:给线程设置优先级,可以大概率的确定谁先执行完; 但不是绝对的
//案例:创建两个子线程,分别打印1~200,并设置优先级,查看执行结果
//分析:创建一个线程类,new两次;其中一个设置高的优先级;一个设置低的优先级
//细节:
//1.调用设置优先级方法:setPriority
//2.给定线程名:
class MyThread extends Thread{
//private String name;
public MyThread(String name) {
//this.name = name;
super(name); //将线程名的值,传给父类的name属性
}
@Override
public void run() {
for(int i=1;i<=200;i++) {
//调父类的getName方法,适用继承Thread方式
//System.out.println(super.getName()+"---->"+i);
//通用方式:适用继承Thread与实现任务方式
System.out.println(Thread.currentThread().getName()+"===>"+i);
}
}
}
public class Test1 {
public static void main(String[] args) {
MyThread thread1 = new MyThread("线程1");
thread1.setPriority(Thread.MIN_PRIORITY); //设置小优先级
thread1.start();
MyThread thread2 = new MyThread("线程2");
thread2.setPriority(Thread.MAX_PRIORITY); //设置优先级,再启动
thread2.start();
}
}
//1.3. 线程的礼让
//线程的礼让: yield
//设置了礼让的线程,会将线程资源让一次出去,继续跟其他线程争抢;
//会影响执行效率,但不是绝对性的
//案例:创建两个子线程,分别打印1~200,其中一个线程,每打印一次则yield一次,查看结果
//分析:创建两个线程继承Thread,其中一个设置礼让
class A extends Thread{
@Override
public void run() {
for(int i=1;i<=200;i++) {
System.out.println("设置礼让线程1===>"+i);
Thread.yield(); //礼让
}
}
}
class B extends Thread{
@Override
public void run() {
for(int i=1;i<=200;i++) {
System.out.println("线程2===>"+i);
}
}
}
public class Test2 {
public static void main(String[] args) {
new A().start();
new B().start();
}
}
//1.4 线程的合并
//线程的合并(插队)--join
//在线程中设置了插队后,插队的线程绝对性地先执行完;
//案例:主线程和子线程,共同执行1~200,主线程执行到50后,子线程插主线程的队;子线程绝对先执行完
//说明:
//1. 最终的结论肯定是插队的子线程先执行完
//2. 过程有两种情况:
//a.子线程一次性执行完,再执行主线程的
//b.主线程执行到50,主线程会停下来,让插队的子线程先执行完
class SonThread extends Thread{
@Override
public void run() {
for(int i=1;i<=200;i++) {
System.out.println("子线程===>"+i);
}
}
}
public class Test3 {
public static void main(String[] args) throws InterruptedException {
SonThread son = new SonThread();
son.start();
for(int i=1;i<=200;i++) {
System.out.println("主线程===>"+i);
if(i==50) {
son.join(); //son子线程插主线程的队
}
}
}
}
//2. 线程安全(重点)
//2.1 多线程存储数组元素的数据安全问题
//线程安全案例:
//案例:在线程中给定一个数组,两个线程同时往数组中存元素
//分析:先执行的线程应该存储第一个下标位置,后执行的线程,应该存储第二个下标位置
//问题:还没来得及下标的累加,可能都存储到了第一个位置---因为线程具有随机互抢特性
//代码应用:
//问题1:打印数组时,可能数据还没有存储
//解决: 想个办法先执行子线程的存储;再执行主线程的打印---join
//问题2:new两个对象,线程中对象的属性都有两份,没办法共享数据
//解决:将线程的属性变为static
//问题3:数据出现问题,都存储到第一个位置了
//加锁:
//锁: 同步代码块,同步方法
//同步代码块: synchronized("lock") {}
//锁的注意事项: 1.两个线程使用同一把锁(同一个锁对象),2.锁的范围
class MyThread extends Thread{
static String[] s = {"","","","",""}; //数据存数据
static int index; //下标
String value;
public MyThread(String value) {
this.value = value;
}
@Override
public void run() {
//只有一个线程能进入锁,另一个在外面等待,等待锁的线程执行完毕,另一个才能进入
synchronized("lock") { //静态属性,字符串常量
s[index] = value; //将存储与下标累加操作进行捆绑
//睡眠3毫秒,复现有问题的数据
index++;
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
MyThread my1 = new MyThread("hello");
my1.start();
MyThread my2 = new MyThread("world");
my2.start();
my1.join(); //两个子线程都要插主线程的队
my2.join();
//打印数组:
System.out.println(Arrays.toString(MyThread.s));
}
}
//2.2 继承Thread方式实现线程安全
//线程安全案例: 5个窗口共同卖1000张票
/*例如: 001窗口正在卖第1000张票
* 003窗口正在卖第999张票
* 005窗口正在卖第998张票
* 。。。
* 002窗口正在卖第1张票
*
*1. 继承Thread方式
*
*问题1: 每个对象都会卖1000张,共5000张
*解决方案: ticket属性+static修饰,变为了5个线程只卖1000张
*
*问题2:出现部分重票的问题---数据安全问题
*解决方案: 加锁
*锁的注意事项:
*1.同一把锁(静态属性,常量值)
*2.锁的范围,锁整个while还是whlie里面的语句(锁住里面,如果锁while,则只有一个窗口卖)
*
*问题3: 会出现负数和0? 临界点问题
*原因: 当票到达1张时,所有线程都可进入到while判断ticket>0,只是一个线程进入锁,其他在锁外等待
*解决: 在锁内部加入if(ticket>0)的判断
*
*问题4: 优化,有多少个窗口卖,必须有多少个窗口退出
*解决方案:加入else提示,将while做一个死循环,在else中break
*
*/
class MyThread extends Thread{
private static int ticket = 1000;
public MyThread(String name) {
super(name);
}
@Override
public void run() {
//锁方式1:同步代码块
/*
while(true) {
synchronized ("lock") {
if(ticket>0) {
System.out.println(super.getName()+"窗口正在卖第"+ticket+"张票");
ticket--;
}else {
System.out.println(super.getName()+"窗口已经卖完了");
break;
}
}
}*/
//锁方式2: 同步方法:也要注意同一把锁(调用方法的对象-this有问题,需加static),和锁的范围
while(true) {
if(save()) { //在同步方法调用中要进行返回值判断
break;
}
}
}
//同步方法的实现
private static synchronized boolean save() {
if(ticket>0) {
System.out.println(Thread.currentThread().getName()+"窗口正在卖第"+ticket+"张票");
ticket--;
return false;
}else {
System.out.println(Thread.currentThread().getName()+"窗口已经卖完了");
return true;
}
}
}
public class Test1 {
public static void main(String[] args) {
for(int i=1;i<=5;i++) {
new MyThread("00"+i).start();
}
}
}
//2.3 实现任务方式完成线程安全
//通过实现任务的方式完成卖票系统的案例:
//分析:创建一个Task类实现Runnable接口
//与继承Thread的区别
//1.多个线程操作同一个Task任务,所以属性无需加static
//2.同步代码块的锁对象可以用this
//3.同步方法无需加static
class Task implements Runnable{
private int ticket = 1000;
@Override
public void run() {
/*
while(true) {
//锁方式1:同步代码块
synchronized (this) {
if(ticket>0) {
System.out.println(Thread.currentThread().getName()+"窗口正在卖第"+ticket+"张票");
ticket--;
}else {
System.out.println(Thread.currentThread().getName()+"窗口已经卖完了");
break;
}
}
}
*/
//锁方式2: 同步方法:也要注意同一把锁(调用方法的对象-this有问题,需加static),和锁的范围
while(true) {
if(save()) { //在同步方法调用中要进行返回值判断
break;
}
}
}
//同步方法的实现
private synchronized boolean save() {
if(ticket>0) {
System.out.println(Thread.currentThread().getName()+"窗口正在卖第"+ticket+"张票");
ticket--;
return false;
}else {
System.out.println(Thread.currentThread().getName()+"窗口已经卖完了");
return true;
}
}
}
public class Test2 {
public static void main(String[] args) {
Task task = new Task();
for(int i=1;i<=5;i++) {
new Thread(task,"00"+i).start(); //多个线程共同操作同一个任务
}
}
}
//3. 死锁
//死锁案例: 线程的双方都握着对方的资源,都退不出去,最后形成了死锁---锁嵌套
//分析:
//创建一个类继承Thread,里面有一个属性,用于做判断的
//实例化两个线程,传递参数,1个为true,一个为false
//判断中,一个先执行A锁,一个先执行B锁的锁嵌套
//注意:学习死锁的目的,就是为了规避死锁
//死锁分析:
//1.当一个线程进入A锁,又直接进入B锁,则锁不住
//Thread-0--进入了A锁
//Thread-0--进入了B锁
//Thread-1--进入了B锁
//Thread-1--进入了A锁
//2.当一个线程进入A锁,另一个线程同时进入B锁,则死锁
//Thread-1--进入了B锁
//Thread-0--进入了A锁
class MyThread extends Thread{
private boolean flag;
public MyThread(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if(flag) {
synchronized ("A") {
System.out.println(super.getName()+"--进入了A锁");
synchronized ("B") {
System.out.println(super.getName()+"--进入了B锁");
}
}
}else {
synchronized ("B") {
System.out.println(super.getName()+"--进入了B锁");
synchronized ("A") {
System.out.println(super.getName()+"--进入了A锁");
}
}
}
}
}
public class Test1 {
public static void main(String[] args) {
new MyThread(true).start();
new MyThread(false).start();
}
}
Java程序猿必学第十八篇—— 多线程
猜你喜欢
转载自blog.csdn.net/m0_62718093/article/details/121127920
今日推荐
周排行