JAVA多线程的知识点概括(随笔)

一、线程的基本概念
  • 线程就是独立的执行路径;
  • 方法间的调用,即从哪里来到哪里去,是闭合的一条路径;多线程的使用则开辟了多条路径
  • 在程序运行时,即使没有自己创建线程,后台也会存在多个线程,如gc进程、主线程(main为系统的入口点,用于执行整个程序);
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的;
  • 很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核。如果是模拟出来的多线程,即一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换得很快,所以就有同时执行得错觉;
二、线程的创建
(一)继承Thread类

Thread的构造器:

在这里插入图片描述
例题:

package thread_study01;
/*
创建线程方式一:
    1.创建:继承Thread+重写run
    2.启动:创建子类对象+start
 */
public class StartThread extends Thread{
    //线程入口点
    public void run(){
        for(int i=0;i<10000;i++){
            System.out.println("一遍听课");
        }
    }
    public static void main(String[] args){
        //启动线程
        //创建子类对象
        StartThread st=new StartThread();
        //启动
        st.start();
        //st.run();//普通方法调用
        for(int i=0;i<10000;i++){
            System.out.println("一边coding");
        }
    }
}

注意:

  • 执行线程必须调用start(),加入到调度器中。
  • 加入后不一定立即执行,系统安排调度分配执行。
  • 直接调用run()不是开启多线程,是普通方法调用。
(二)实现Runnable接口

例题:

package thread_study01;
/*
创建线程方式二:
    1.创建:实现Runnable+重写run
    2.启动:创建实现类对象+Thread对象+start
    推荐 避免单继承的局限性,优先使用接口
    方便共享资源
 */
public class StartRun implements Runnable{
    //线程入口点
    public void run(){
        for(int i=0;i<10000;i++){
            System.out.println("一遍听课");
        }
    }
    public static void main(String[] args){
        new Thread(new StartRun()).start();
        for(int i=0;i<10000;i++){
            System.out.println("一边coding");
        }
    }
}
(三)实现Callable接口

例题:

package thread_study01;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
 //了解创建线程的方式三: 
public class CDownloader implements Callable<Boolean>{
	private String url; //远程路径
	private String name;  //存储名字
	public CDownloader(String url, String name) {
		this.url = url; 
		this.name = name;
	}
	@Override
	public Boolean call() throws Exception {
		WebDownloader wd =new WebDownloader();
		wd.download(url, name);		
		System.out.println(name);
		return true;
	}
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		CDownloader cd1 =new CDownloader("http://upload.news.cecb2b.com/2014/0511/1399775432250.jpg","phone.jpg");
		CDownloader cd2 =new CDownloader("http://p1.pstatp.com/large/403c00037462ae2eee13","spl.jpg");
		CDownloader cd3 =new CDownloader("http://5b0988e595225.cdn.sohucs.com/images/20170830/d8b57e0dce0d4fa29bd5ef014be663d5.jpeg","success.jpg");
		//创建执行服务: 
		ExecutorService  ser=Executors.newFixedThreadPool(3);
		//提交执行: 
		Future<Boolean> result1 =ser.submit(cd1) ;
		Future<Boolean> result2 =ser.submit(cd2) ;
		Future<Boolean> result3 =ser.submit(cd3) ;
		//获取结果:  
		boolean r1 =result1.get();
		boolean r2 =result1.get();
		boolean r3 =result1.get();
		System.out.println(r3);
		//关闭服务:  
		ser.shutdownNow();
	}
}
(四)补充

1.静态代理:
例题:

package thread_study01;
/**
* 静态代理
* 接口:
* 1.真实角色
* 2.代理角色
*/
public class StaticProxy {
   public static void main(String[] args) {
       new WeddingCompany(new You()).happyMarry();
   }
}
//接口
interface Marry{
   void happyMarry();
}
//真实角色
class You implements Marry{
   @Override
   public void happyMarry(){
       System.out.println("......you and 嫦娥终于奔月了......");
       }
}
//代理角色
class WeddingCompany implements Marry{
   //真实角色
   private Marry target;
   public WeddingCompany(Marry target){
       this.target=target;
   }
   @Override
   public void happyMarry() {
       ready();
       target.happyMarry();
       after();
   }
   private void  ready(){
       System.out.println("布置猪窝。。。。");
   }
   private void after(){
       System.out.println("闹玉兔。。。。");
   }
}

2.lambda:
lambda的是为了避免匿名内部类定义过多,其实质属于函数式编程的概念。
例题:
lambda表达式的推导过程

package thread_study01;
// Lambda表达式 简化线程(用一次)的使用
public class LambdaThread{
   //静态内部类
   static class Test implements Runnable{
       public void run(){
           for(int i=0;i<10000;i++){
               System.out.println("一遍听课");
           }
       }
   }
   public static void main(String[] args){
       new Thread(new Test()).start();
       //局部内部类
       class Test2 implements Runnable{
           public void run(){
               for(int i=0;i<10000;i++){
                   System.out.println("一遍听课");
               }
           }
       }
       new Thread(new Test2()).start();
       //匿名内部类 必须借助接口或者父类
       new Thread(new Runnable() {
           @Override
           public void run() {
               for(int i=0;i<10000;i++){
                   System.out.println("一遍听课");
               }
           }
       }).start();
       //jdk8 简化 lambda
       new Thread(()->{
               for(int i=0;i<10000;i++){
                   System.out.println("一遍听课");
               }
           }
       ).start();
   }
}
三、线程状态
(一)线程各状态间的关系:

在这里插入图片描述
在这里插入图片描述

(二)sleep
package thread_study02;

 // sleep模拟网络延时,放大了发生问题的可能性

public class BlockedSleep {
    public static void main(String[] args){
        //一份资源
        Web12306 web=new Web12306();
        //多个代理
        new Thread(web,"码农").start();
        new Thread(web,"码畜").start();
        new Thread(web,"码蟥").start();
    }

}
class Web12306 implements Runnable {
    //票数
    private int ticketNum = 1000;
    @Override
    public void run() {
        while (true) {
            if (ticketNum < 0)
                break;
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "-->" + ticketNum--);
        }
    }
}
  • sleep(时间)指定当前线程阻塞的毫秒数;
  • sleep存在异常InterruptedException;
  • sleep到达后线程进入就绪状态
  • 可模拟网络延迟、倒计时等
  • 每一个对象都是一把锁,sleep不会释放锁
(三)join

join合并线程,待此线程执行完成后,再执行其它线程,其它线程阻塞
例题:

package thread_study02;

 // join:合并线程, 插队线程
public class BlockedJoin01 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
           for(int i=0;i<100;i++)
               System.out.println("lambda..."+i);
        });
        t.start();
        for (int i=0;i<100;i++){
            if(i==20){
                t.join();//插队 main被阻塞了
            }
            System.out.println("main"+i);
        }
    }
}
(四)yield
package thread_study02;

public class YieldDemo02 {
    public static void main(String[] args) {
        new Thread(()->{
           for (int i=0;i<100;i++)
               System.out.println("lambda...");
        }).start();

        for (int i=0;i<100;i++) {
            if(i%20==0)
                Thread.yield();//main礼让
            System.out.println("main....");
        }
    }
}
  • 礼让线程,让当前正在执行线程暂停
  • 不是阻塞线程,而是将线程从运行状态转入就绪状态
  • 让cpu调度器重新调度
(五)stop
  • 不使用JDK提供的stop()/destroy()方法(他们本身也被JDK废弃了)
  • 提供一个boolean型的终止变量,当这个变量置为false,则终止线程的运行
    例题:
package thread_study02;

/**
 * 终止线程
 * 1.线程正常执行完毕-->次数
 * 2.外部干涉-->标识
 * 不要使用stop destroy
 */
public class TerminateThread implements Runnable {
    //1.创建标识
    private boolean flag=true;
    private String name;

    public TerminateThread(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        int i=0;
        //关联标识,true-->运行 false-->停止
        while (flag)
            System.out.println(name+"-->"+i++);
    }
    //3.对外提供方法改变标识
    public void terminate(){
        flag=false;
    }
    public static void main(String[] args) {
        TerminateThread tt=new TerminateThread("C罗");
        new Thread(tt).start();
        for(int i=0;i<=999;i++){
            if(i==888){
                tt.terminate();//线程的终止
                System.out.println("tt game over");
            }
            System.out.println("main"+i);
        }
    }
}
(六)priority

优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高后调用优先级低的线程。
例子:

package thread_study02;

/**
 * 线程的优先级1-10
 * 1.NORM_PRIORITY  5
 * 2.MIN_PRIORITY 1
 * 3.MAX_PRIORITY 10
 * 概率,不代表绝对先后
 */
public class PriorityTest {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getPriority());
        MyPriority mp=new MyPriority();

        Thread t1=new Thread(mp,"adidas");
        Thread t2=new Thread(mp,"NINE");
        Thread t3=new Thread(mp,"匡威");
        Thread t4=new Thread(mp,"puma");
        Thread t5=new Thread(mp,"李宁");
        Thread t6=new Thread(mp,"回力");
        //设置优先级要在运行前
        t1.setPriority(10);
        t2.setPriority(Thread.MAX_PRIORITY);
        t3.setPriority(Thread.MAX_PRIORITY);
        t4.setPriority(1);
        t5.setPriority(Thread.MIN_PRIORITY);
        t6.setPriority(Thread.MIN_PRIORITY);
        t1.start();t2.start();t3.start();t4.start();t5.start();t6.start();
    }
}
class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
        Thread.yield();
    }
}
(七)daemon
  • 线程分为用户进程和守护进程
  • 虚拟机必须确保用户进程执行完毕
  • 虚拟机不用等待守护进程执行完毕
    例子:
package thread_study02;

/**
 * 守护线程:是为用户线程服务的;jvm停止不用等待守护线程执行完毕
 * 默认:用户线程 jvm等待用户线程执行完毕
 */
public class DaemonTest {
    public static void main(String[] args) {
        God god=new God();
        You you=new You();
        Thread t=new Thread(god);
        t.setDaemon(true);//将用户进程调整为守护进程
        t.start();
        new Thread(you).start();
    }
}
class You implements Runnable{
    @Override
    public void run() {
        for (int i=1;i<365*100;i++){
            System.out.println("happy life...");
        }
        System.out.println("ooooooo");
    }
}
class God implements Runnable{
    @Override
    public void run() {
        for(;true;){
            System.out.println("bless you");
        }
    }
}
(八)其它常用方法

例子:

package thread_study02;

/**
 * 其他方法
 * isAlive:线程是否还活着
 * Thread.currentThread();当前线程
 * setName、getName():代理名称
 */
public class InfoTest {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(Thread.currentThread().isAlive());

        //设置名称:真实角色+代理角色
        MyInfo info=new MyInfo("战斗机");
        Thread t=new Thread(info);
        t.setName("公鸡");
        t.start();
        Thread.sleep(1000);
        System.out.println(t.isAlive());
    }
}
class MyInfo implements Runnable{
    private String name;
    public MyInfo(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->"+name);
    }
}
四、线程同步
(一)synchronized方法

synchronized方法控制对“成员变量|类变量”对象的访问:每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。
例子:

package thread_study03;

/**
 *线程安全:在并发时保证数据的正确性、效率尽可能高
 * synchronized同步方法
 */
public class SynsafeTest01 {
    public static void main(String[] args){
        //一份资源
        SafeWeb12306 web=new SafeWeb12306();
        //多个代理
        new Thread(web,"码农").start();
        new Thread(web,"码畜").start();
        new Thread(web,"码蟥").start();
    }
}
class SafeWeb12306 implements Runnable {
    //票数
    private int ticketNum=10;
    private boolean flag=true;
    @Override
    public void run() {
        while(flag){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            test();
        }
    }
    //线程安全 同步
    public synchronized void test(){
        if(ticketNum<0) {
            flag = false;
            return;
        }
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"-->"+ticketNum--);
    }
}
(二)synchronized块

同步块:synchronized(obj){ },obj称之为同步监视器

  • obj可以是任何对象,但是推荐使用共享资源作为同步监视器。
  • 同步方法中无需指定同步监视器,因为同步方法的同步监视器是this即该对象本身,或class即类的模子。
    同步监视器的执行过程
  • 第一个线程访问,锁定同步监视器,执行其中代码
  • 第二个线程访问,发现同步监视器被锁定,无法访问
  • 第一个线程访问完毕,解锁同步监视器
  • 第二个线程访问,发现同步监视器未锁,锁定并访问
    例子:
 package thread_study03;

public class SynBlockTest01 {
    public static void main(String[] args) {
        //账户
        Account account=new Account(100,"结婚礼金");
        SynDrawing you=new SynDrawing(account,80,"可悲的你");
        SynDrawing wife=new SynDrawing(account,90,"happy的她");
        you.start();
        wife.start();
    }
}
//模拟取款 线程安全
class SynDrawing extends Thread{
    Account account;//取钱的账户
    int drawingMoney;//取得钱数
    int packetTotal=0;//口袋的总数
    public SynDrawing(Account account, int drawingMoney, String name) {
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }
    @Override
    public void run() {
        test();
    }
    //目标锁定account
    public void test(){
        //提高性能代码
        if(account.money<=0){
            return;
        }
        synchronized (account) {
            if (account.money - drawingMoney < 0) {
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.money -= drawingMoney;
            packetTotal += drawingMoney;
            System.out.println(this.getName() + "口袋的钱为:" + packetTotal);
            System.out.println(this.getName() + "账户余额为:" + account.money);
        }
    }
}
(三)死锁

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或多个线程都在等待对方释放资源,都停止执行的情形。
例题:

package thread_study03;
/**
 * 死锁: 过多的同步可能造成相互不释放资源
 * 从而相互等待,一般发生于同步中持有多个对象的锁
 * 避免: 不要在同一个代码块中,同时持有多个对象的锁
 */
public class DeadLock {

	public static void main(String[] args) {
		Markup g1 = new Markup(1,"张柏芝");
		Markup g2 = new Markup(0,"王菲");
		g1.start();
		g2.start();
	}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
//化妆
class Markup extends Thread{ 
	static Lipstick lipstick = new Lipstick();
	static Mirror mirror = new Mirror();
	//选择
	int choice;
	//名字
	String girl;
	public Markup(int choice,String girl) {
		this.choice = choice;
		this.girl = girl;
	}

	@Override
	public void run() {
		//化妆
		markup();
	}
	//相互持有对方的对象锁-->可能造成死锁
	private void markup() {
		if(choice==0) {
			synchronized(lipstick) { //获得口红的锁
				System.out.println(this.girl+"涂口红");
				//1秒后想拥有镜子的锁
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				/*
				synchronized(mirror) {
					System.out.println(this.girl+"照镜子");
				}*/			
			}
			synchronized(mirror) {
				System.out.println(this.girl+"照镜子");
			}		
		}else {
				synchronized(mirror) { //获得镜子的锁
					System.out.println(this.girl+"照镜子");
					//2秒后想拥有口红的锁
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					/*
					synchronized(lipstick) {
						System.out.println(this.girl+"涂口红");
					}	*/	
			}
				synchronized(lipstick) {
					System.out.println(this.girl+"涂口红");
				}
		}
	}
}
五、线程协作

线程通信

(一)管道法

例题:

package thread_study04;
/**
 * 协作模型:生产者消费者实现方式一:管程法
 * 借助缓冲区
 */
public class CoTest01 {
	public static void main(String[] args) {
		SynContainer container = new SynContainer();
		new Productor(container).start();
		new Consumer(container).start();
	}
}
//生产者
class Productor extends Thread{
	SynContainer container  ;	
	public Productor(SynContainer container) {
		this.container = container;
	}
	public void run() {
		//生产
		for(int i=0;i<100;i++) {
			System.out.println("生产-->"+i+"个馒头");
			container.push(new Steamedbun(i) );
		}
	}
}
//消费者
class Consumer extends Thread{
	SynContainer container  ;	
	public Consumer(SynContainer container) {
		this.container = container;
	}
	public void run() {
		//消费
		for(int i=0;i<100;i++) {
			System.out.println("消费-->"+container.pop().id+"个馒头");
		}
	}
}
//缓冲区
class SynContainer{
	Steamedbun[] buns = new Steamedbun[10]; //存储容器
	int count = 0; //计数器
	//存储 生产
	public synchronized void push(Steamedbun bun) {
		//何时能生产  容器存在空间
		//不能生产 只有等待
		if(count == buns.length) {
			try {
				this.wait(); //线程阻塞  消费者通知生产解除
			} catch (InterruptedException e) {
			}
		}
		//存在空间 可以生产
		buns[count] = bun;
		count++;
		//存在数据了,可以通知消费了
		this.notifyAll();
	}
	//获取 消费
	public synchronized Steamedbun pop() {
		//何时消费 容器中是否存在数据
		//没有数据 只有等待
		if(count == 0) {
			try {
				this.wait(); //线程阻塞  生产者通知消费解除
			} catch (InterruptedException e) {
			}
		}
		//存在数据可以消费
		count --;
		Steamedbun bun = buns[count] ;		
		this.notifyAll(); //存在空间了,可以唤醒对方生产了
		return bun;
	}
}
//馒头
class Steamedbun{
	int id;
	public Steamedbun(int id) {
		this.id = id;
	}	
}

提示:与sleep不同,wait会释放锁

(二)信号灯法

例题:

package thread_study04;
/**
 * 协作模型:生产者消费者实现方式二:信号灯法
 * 借助标志位
 */
public class CoTest02 {
	public static void main(String[] args) {
		Tv tv  =new Tv();
		new Player(tv).start();
		new Watcher(tv).start();
	}
}
//生产者 演员
class Player extends Thread{
	Tv tv;	
	public Player(Tv tv) {
		this.tv = tv;
	}
	public void run() {
		for(int i=0;i<20;i++) {
			if(i%2==0) {
				this.tv.play("奇葩说");
			}else {
				this.tv.play("太污了,喝瓶立白洗洗嘴");
			}
		}
	}
}
//消费者 观众
class Watcher extends Thread{
	Tv tv;	
	public Watcher(Tv tv) {
		this.tv = tv;
	}
	public void run() {
		for(int i=0;i<20;i++) {
			tv.watch();
		}
	}
}
//同一个资源 电视
class Tv{
	String voice;
	//信号灯
	//T 表示演员表演 观众等待
	//F 表示观众观看 演员等待
	boolean flag = true;
	//表演
	public  synchronized void play(String voice) {
		//演员等待
		if(!flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}		
		//表演
		System.out.println("表演了:"+voice);
		this.voice = voice;
		//唤醒
		this.notifyAll();
		//切换标志
		this.flag =!this.flag;
	}
	//观看
	public synchronized  void watch() {
		//观众等待
		if(flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//观看
		System.out.println("听到了:"+voice);
		//唤醒
		this.notifyAll();
		//切换标志
		this.flag =!this.flag;
	}
}
六、高级主题
(一)任务定时调度

例题一:

package thread_study04;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Timer;
import java.util.TimerTask;
 // 任务调度: Timer(本身就是一个线程) 和TimerTask类(实现了Runnable接口,具备多线程的能力)
public class TimerTest01 {

	public static void main(String[] args) {
		Timer timer = new Timer();
		//执行安排
		//timer.schedule(new MyTask(), 1000);  //执行任务一次
		//timer.schedule(new MyTask(), 1000,200); //执行多次
		Calendar cal = new GregorianCalendar(2099999,12,31,21,53,54);
		timer.schedule(new MyTask(), cal.getTime(),200); //指定时间
	}

}
//任务类
class  MyTask extends TimerTask{
	@Override
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println("放空大脑休息一会");
		}
		System.out.println("------end-------");
	}
	
}

例题二(比较难,暂时先做了解):

package thread_study04;

import static org.quartz.DateBuilder.evenSecondDateAfterNow;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
 // quartz学习入门
public class QuartzTest {

  public void run() throws Exception {
    // 1、创建 Scheduler的工厂
    SchedulerFactory sf = new StdSchedulerFactory();
    //2、从工厂中获取调度器
    Scheduler sched = sf.getScheduler();  
    // 3、创建JobDetail
    JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1").build();
    // 时间
    Date runTime = evenSecondDateAfterNow();
    // 4、触发条件
    //Trigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(runTime).build();
    Trigger trigger  = newTrigger().withIdentity("trigger1", "group1").startAt(runTime)
            .withSchedule(simpleSchedule().withIntervalInSeconds(5).withRepeatCount(3)).build();
    // 5、注册任务和触发条件
    sched.scheduleJob(job, trigger);

    // 6、启动
    sched.start();
    try {
      // 100秒后停止
      Thread.sleep(100L * 1000L);
    } catch (Exception e) {
    }
    sched.shutdown(true);
  }

  public static void main(String[] args) throws Exception {
    QuartzTest example = new QuartzTest();
    example.run();
  }
}
(二)HappenBefore(指令重排)

你写的代码很可能根本没按你期望的顺序执行,因为编译器和CPU会尝试重排指令使得代码更快地运行
例题:

package thread_study04;
/**
 * 指令重排: 代码执行顺序与预期不一致
 * 目的:提高性能
 */
public class HappenBefore {
	//变量1
	private  static int a = 0;
	//变量2
	private static boolean flag = false;
	public static void main(String[] args) throws InterruptedException {
		for(int i=0;i<10;i++) {
			a = 0;
			flag = false;
			
			//线程1 更改数据
			Thread t1 = new Thread(()->{
				a = 1;
				flag = true;
			}) ;
			//线程2 读取数据
			Thread t2 = new Thread(()->{
				if(flag) {
					a *=1;
				}
				//指令重排
				if(a == 0) {
					System.out.println("happen before a->"+a);
				}
			}) ;
			t1.start();
			t2.start();
			//合并线程
			t1.join();
			t2.join();
		}
	}
}
(三)Volatile
  • 线程对变量进行修改之后,要立刻写回主内存;
  • 线程对变量读取的时候,要从主内存中读,而不是缓存。
  • 它不能保证原子性。
    例题:
package com.sxt.others;
 // volatile用于保证数据的同步,也就是可见性
public class VolatileTest {
	private volatile static int num = 0;
	public static void main(String[] args) throws InterruptedException {
		new Thread(()->{
			while(num==0) { //此处不要编写代码
				
			}
		}) .start();
		
		Thread.sleep(1000);
		num = 1;
	}
}
(四)单例模式

ThreadLocal能够放一个线程级别的变量,其本身能够被多个线程共享使用,并且又能够达到线程安全的目的。说白了,ThreadLocal就是想在多线程环境下去保证成员变量的安全。
例题:

package com.sxt.others;
/**
 * ThreadLocal:每个线程自身的存储本地、局部区域
 *  get/set/initialValue
 */
public class ThreadLocalTest01 {
	//private static ThreadLocal<Integer> threadLocal = new ThreadLocal<> ();
	//更改初始化值
	/*private static ThreadLocal<Integer> threadLocal = new ThreadLocal<> () {
		protected Integer initialValue() {
			return 200;
		}; 
	};*/
	private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()-> 200);
	public static void main(String[] args) {
		//获取值
		System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());		
		//设置值
		threadLocal.set(99);
		System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
		
		new Thread(new MyRun()).start();
		new Thread(new MyRun()).start();
	}	
	public static  class MyRun implements Runnable{
		public void run() {
			threadLocal.set((int)(Math.random()*99));
			System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());		
		}
	}	
}
  • 每个线程自身的数据,更改不会影响其他线程
  • ThreadLocal要分析上下文 环境 起点1.构造器: 哪里调用 就属于哪里 找线程体 2.run方法:本线程自身的
  • InheritableThreadLocal:继承上下文 环境的数据 ,拷贝一份给子线程
(五)可重入锁

锁作为并发共享数据保证一致性的工具,大多数内置锁都是可重入的,也就是说,如果某个线程试图获取一个已经由他持有的锁时,那么这个请求会立刻成功,并且会将这个锁的计数值加1,而当线程退出同步代码块时,计数器将会递减,当计数值等于0时,锁释放。如果没有可重入锁额支持,在第二次企图获得锁时将会进入死锁状态。
例题:

package com.sxt.others;

 // 可重入锁: 锁可以延续使用 + 计数器
public class LockTest03 {
	ReLock lock = new ReLock();
	public void a() throws InterruptedException {
		lock.lock();
		System.out.println(lock.getHoldCount());
		doSomething();
		lock.unlock();
		System.out.println(lock.getHoldCount());
	}
	//不可重入
	public void doSomething() throws InterruptedException {
		lock.lock();
		System.out.println(lock.getHoldCount());
		//...................
		lock.unlock();
		System.out.println(lock.getHoldCount());
	}
	public static void main(String[] args) throws InterruptedException {
		LockTest03 test = new LockTest03();
		test.a();			
		Thread.sleep(1000);		
		System.out.println(test.lock.getHoldCount());
	}

}
// 可重入锁
class ReLock{
	//是否占用
	private boolean isLocked = false;
	private Thread lockedBy = null; //存储线程
	private int holdCount = 0;
	//使用锁
	public synchronized void lock() throws InterruptedException {
		Thread t = Thread.currentThread();
		while(isLocked && lockedBy != t) {
			wait();
		}
		
		isLocked = true;
		lockedBy = t;
		holdCount ++;
	}
	//释放锁
	public synchronized void unlock() {
		if(Thread.currentThread() == lockedBy) {
			holdCount --;
			if(holdCount ==0) {
				isLocked = false;
				notify();
				lockedBy = null;
			}		
		}		
	}
	public int getHoldCount() {
		return holdCount;
	}
}
(六)CAS

锁分为两类:

  • 悲观锁:synchronized是独占锁,即悲观锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
  • 乐观锁:每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,知道成功为止。
    例题:
package com.sxt.others;

import java.util.concurrent.atomic.AtomicInteger;

 // CAS:比较并交换
public class CAS {
	//库存
	private static AtomicInteger stock = new AtomicInteger(5);
	public static void main(String[] args) {
		for(int i=0;i<5;i++) {
			new Thread(()->{
				//模拟网络延时
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				Integer left = stock.decrementAndGet();
				if(left<1) {
					System.out.println("抢完了...");
					return ;
				}
				System.out.print(Thread.currentThread().getName()+"抢了一件商品");
				System.out.println("-->还剩"+left);
			}) .start();
		}
	}
}
  • 有三个值一个当前值V、旧的预期值A、将更新的值B。首先要获取内存当中当前的内存值V,再将内存值V和原值A作比较,要是相等就修改为要修改的值B并返回true,否则什么也不做并返回false。
  • CAS是一组原子操作,不会被外部打断。
  • 属于硬件级别的操作,效率比加锁高。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值