Java并发-线程基础知识

176 阅读10分钟

线程

新建线程

  • 继承Thread类:
public class newThead{
  private static class UseThread extends Thread{
    @Override
    public void run{
      System.out.printIn("I'm created by extends Thread")
    }
  }
  
 public static void main(String[] args){
   new UseThread().start();
 }
} 

注: Java是单继承的语言,如果继承了Thred 类的话,就不能继承其他类了。

  • 实现Runable接口
public class newThread{
  private static class UseRunnable implements Runnable{
    @Override
    public void run(){
      System.out.printIn("I'm created by implements Runable")
    }
  }
  
  public static void main(Sting[] args){
    new Thread(UseRunable).satrt();
  }
}

注: run方法没有返回值。

  • 实现Callable接口
public class newThread{
	private static class UseCall implements Callable<String>{
		@Override
		public String call() throws Exception {
			System.out.println("I am implements Callable");
			return "CallResult";
		}
	}	
	public static void main(String[] args){
		
		UseCall useCall = new UseCall();
		FutureTask<String> futureTask = new FutureTask<>(useCall);
		new Thread(futureTask).start();
		System.out.println(futureTask.get());
	}
} 

结束线程

线程自然结束有两种情况

  • 线程执行完成,自然结束

  • 抛出未处理异常

    不建议使用stop(),stop()会导致线程不能正确的释放资源,不建议使用suspend(),容易导致死锁

interrupt()isInterrupted()、static方法**interrupted()**的关系与区别

  • interrupt() 中断线程,并不是强行关闭这个线程,而是将线程的中断标志位设为 true ,线程是否中断由线程自己决定。

  • isInterrupted() 判定当前线程是否处于中断状态。

  • static方法interrupted() 判定当前线程是否处于中断状态,同时中断标志位改为false

public class EndThread {
	
	private static class UseThread extends Thread{
		
		public UseThread(String name) {
			super(name);
		}
		
		@Override
		public void run() {
			String threadName = Thread.currentThread().getName();
			while(!Thread.currentThread().isInterrupted()) {
				System.out.println(threadName+" is run!");
			}
		System.out.println(threadName+" interrput flag is "+isInterrupted());
		}
	}

	public static void main(String[] args) throws InterruptedException {
		Thread endThread = new UseThread("endThread");
		endThread.start();
		Thread.sleep(20);
		endThread.interrupt();

	}

}

输出打印结果为

endThread is run!
endThread interrput flag is true

说明我们在调用 endThread.interrupt(); 之后 endThread的中断标志编程true了;

在抛出异常的时候,会自动将线程的中断标志重置为false,案例如下

public class HasInterrputException {
	
	private static SimpleDateFormat formater 
		= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss_SSS");
	
	private static class UseThread extends Thread{
		
		public UseThread(String name) {
			super(name);
		}
		
		@Override
		public void run() {
			String threadName = Thread.currentThread().getName();
			while(!isInterrupted()) {
				try {
					System.out.println("UseThread:"+formater.format(new Date()));
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					System.out.println(threadName+" catch interrput flag is "
							+isInterrupted()+ " at "
							+(formater.format(new Date())));
					interrupt();
					e.printStackTrace();
				}
				System.out.println(threadName);				
			}
			System.out.println(threadName+" interrput flag is "
					+isInterrupted());
		}
	}

	public static void main(String[] args) throws InterruptedException {
		Thread endThread = new UseThread("HasInterrputEx");
		endThread.start();
		System.out.println("Main:"+formater.format(new Date()));
		Thread.sleep(800);
		System.out.println("Main begin interrupt thread:"+formater.format(new Date()));
		endThread.interrupt();
		

	}

输出结果为:
    
UseThread:2020-04-01 17:06:49_682
Main:2020-04-01 17:06:49_682
Main begin interrupt thread:2020-04-01 17:06:50_502
HasInterrputEx catch interrput flag is false at 2020-04-01 17:06:50_502
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at com.xiangxue.ch1.safeend.HasInterrputException$UseThread.run(HasInterrputException.java:28)
HasInterrputEx
HasInterrputEx interrput flag is true

Process finished with exit code 0

线程的状态

线程的状态如下图所示,线程在不同的状态之间切换。

image-20200407171429596

线程中start与run方法的区别

run()方法只是普通对象里面普通的方法,只有调用了start(),之后JVM会将线程对象和操作系统里面的实际线程进行映射,再执行run方法。

yild()

让出cpu的执行权,将线程从「运行状态」转换为「可运行状态」 ,该线程已让有可能被再次选中。

线程的优先级

取值为1-10,缺省为5,但是线程的优先级不可靠,不建议作为线程开发时候的手段。

守护线程

和主线程共死,finally 不能保证一定执行。

public class DaemonThread {
	
	private static class UseThread extends Thread {
		@Override
		public void run() {
			try {
				while (!isInterrupted()) {
					System.out.println(Thread.currentThread().getName()
							+ " I am extends Thread.");
				}
				System.out.println(Thread.currentThread().getName() 
						+ " interrupt flag is " + isInterrupted());
			} finally {
				System.out.println("...........finally");
			}
		}
	}

	public static void main(String[] args) throws InterruptedException, 
		ExecutionException {
		UseThread useThread = new UseThread();
		useThread.setDaemon(true);
		useThread.start();
		Thread.sleep(5);
		useThread.interrupt();
	}
}

线程之间的共享

synchronized
  • 对象锁,锁的是对象的实例,代码如下

    public class SynInstance {
    
        private static class InstanceSyn implements Runnable{
    
            private SynInstance synInstance;
    
            public InstanceSyn(SynInstance synInstance){
                this.synInstance = synInstance;
            }
    
            @Override
            public void run() {
                System.out.println("TestInstance is running ... "+ synInstance);
                synInstance.instance();
            }
        }
    
    
        private  static  class Instance2Syn implements  Runnable{
            private SynInstance synInstance;
            public Instance2Syn(SynInstance synInstance) {
                this.synInstance = synInstance;
            }
    
            @Override
            public void run() {
                System.out.println("TestInstance is running ......"+synInstance);
                synInstance.instance2();
            }
        }
    
    
        private synchronized void instance(){
            SleepTools.second(3);
            System.out.println("synInstance is going ...."+this.toString());
            SleepTools.second(3);
            System.out.println("synInstance end "+ this.toString());
        }
    
        private synchronized void instance2(){
            SleepTools.second(3);
            System.out.println("synInstance2 is going ...."+this.toString());
            SleepTools.second(3);
            System.out.println("synInstance2 end "+ this.toString());
    
        }
    
        public static void main (String[] args){
            SynInstance synInstance = new SynInstance();
            Thread t1 = new Thread(new InstanceSyn(synInstance));
    
    
    //        Thread t2  = new Thread(new Instance2Syn(new SynInstance()));  1⃣️
    //        Thread t2  = new Thread(new Instance2Syn(synInstance)); 2⃣️
    
            t1.start();
            t2.start();
            SleepTools.second(1);
        }
    }
    

    若运行1⃣️的话结果如下

    TestInstance is running ......com.sakura.ch1.syn.SynInstance@10d9dd76
    TestInstance is running ... com.sakura.ch1.syn.SynInstance@1374ac79
    synInstance is going ....com.sakura.ch1.syn.SynInstance@1374ac79
    synInstance2 is going ....com.sakura.ch1.syn.SynInstance@10d9dd76
    synInstance end com.sakura.ch1.syn.SynInstance@1374ac79
    synInstance2 end com.sakura.ch1.syn.SynInstance@10d9dd76
    

    若运2⃣️的话,结果如下

    TestInstance is running ... com.sakura.ch1.syn.SynInstance@29046243
    TestInstance is running ......com.sakura.ch1.syn.SynInstance@29046243
    synInstance is going ....com.sakura.ch1.syn.SynInstance@29046243
    synInstance end com.sakura.ch1.syn.SynInstance@29046243
    synInstance2 is going ....com.sakura.ch1.syn.SynInstance@29046243
    synInstance2 end com.sakura.ch1.syn.SynInstance@29046243
    
  • 类锁 锁的是每个类的Class对象,每个类的Class对象在虚拟机中只有一个,所以类锁只有一个,代码如下

    public class SynClass {
        private static class SynCl extends Thread{
            @Override
            public void run() {
                System.out.println("TestClass is running .....");
                synCl();
            }
        }
    
        private static synchronized void synCl(){
            SleepTools.second(1);
            System.out.printf("synClass going....");
            SleepTools.second(1);
            System.out.println("synClass end");
        }
    
        public static  void main(String[] args){
            SynCl synClass = new SynCl();
            synClass.start();
            SynCl  synCl = new SynCl();
            synCl.start();
    
            SleepTools.second(1);
        }
    }
    

    输出结果为

    TestClass is running .....
    TestClass is running .....
    synClass going....synClass end
    synClass going....synClass end
    
Volatile

JVM提供的最轻量的同步机制,适合于只有一个线程写,多个线程读的场景,因为它只能确保可见性。并不是线程安全的,

image-20200407172740326

public class VolaUnSafe {


    private static class VolatileVar implements Runnable{
        private volatile int a = 0;
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            a = a++;
            System.out.println(threadName+"-----"+a);
            a = a+1;
            System.out.println(threadName+"-----"+a);
        }
    }

    public static void main(String[] args){
        VolatileVar v = new VolatileVar();

        Thread t1 = new Thread(v);
        Thread t2 = new Thread(v);
        Thread t3 = new Thread(v);
        Thread t4 = new Thread(v);
        Thread t5 = new Thread(v);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}

输出结果会混乱如下

Thread-0-----0
Thread-4-----0
Thread-0-----1
Thread-1-----0
Thread-2-----0
Thread-3-----0
Thread-3-----5
Thread-2-----4
Thread-1-----3
Thread-4-----2
ThreadLocal

线程内部的变量,ThreadLocal类里面有个ThreadLocalMap<Thread,Integer>,他的键为Thread对象,值为该变量在该线程中的值,运用代码如下

public class UseThreadLocal {
    static final  ThreadLocal<Integer> a = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };

    private static class TestThread implements Runnable{

        int id;

        public TestThread(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" start1");
            Integer s = a.get();
            s = s + id;
            a.set(s);
            System.out.println(Thread.currentThread().getName()+":"+a.get());

        }
    }


    private void startThreeThread(){
        Thread[] runs = new Thread[3];
        for(int i = 0;i< runs.length;i++){
            runs[i] = new Thread(new TestThread(i));
        }
        for(int j = 0;j<runs.length;j++){
            runs[j].start();
        }
    }
    public static void main(String[] args){
        UseThreadLocal useThreadLocal = new UseThreadLocal();
        useThreadLocal.startThreeThread();
    }
}

输出结果为

Thread-0 start1
Thread-2 start1
Thread-1 start1
Thread-1:2
Thread-2:3
Thread-0:1

线程之间的协作

wait() notify()/notifyAll()

这三个都是对象上的方法,wait()阻塞当前线程,交出执行权,释放锁。notify()是唤醒某个wait()的线程,notifyAll(),是唤醒所有的wait()线程。notify和notifyAll应该用谁?

应该尽量使用notifyAll,使用notify因为有可能发生信号丢失的的情况

等待通知的标准范式

等待方:

1、 获取对象的锁;

2、 循环里判断条件是否满足,不满足调用wait方法,

3、 条件满足执行业务逻辑

通知方来说

1、 获取对象的锁;

2、 改变条件

3、 通知所有等待在对象的线程

具体使用如下代码:

Express.class 快递邮件对象

public class Express {
    public static final String CITY="shanghai";

    private int km;

    private String site;

    public Express(int km, String site) {
        this.km = km;
        this.site = site;
    }

    public synchronized void changeKm(){
        this.km = 101;
        notify();//1⃣️
      //notifyAll() 2⃣️
    }

    public synchronized void changeSite(){
        this.site = "beijing";
        notify ();//1⃣️
      //notifyAll() 2⃣️
    }


    public synchronized void waitKm(){
        while (km <= 100){
            try {
                wait();
                System.out.println("checkKM  Thread ["+Thread.currentThread().getId()+" ] is be notified");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("the km is "+this.km+",i will change DB");
    }


    public synchronized void waitSite(){
        while (CITY.equals(this.site)){
            try {
                wait();
                System.out.println("checkSite  Thread ["+Thread.currentThread().getId()+" ] is be notified");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("the site is "+this.km+",i will call user");
    }
}

调用

public class TestWN {

    private static Express express = new Express(100,Express.CITY);

    private static class CheckKm extends Thread{
        @Override
        public void run() {
            express.waitKm();
        }
    }


    private static class CheckSite extends Thread{
        @Override
        public void run() {
            express.waitSite();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0;i < 3;i++){
            new CheckSite().start();
        }
        for (int i = 0;i < 3;i++){
            new CheckKm().start();
        }

        Thread.sleep(1000);
        express.changeKm();

    }
}

运行1⃣️输出如下

checkSite  Thread [11 ] is be notified

运行2⃣️输出如下

checkKM  Thread [16 ] is be notified
the km is 101,i will change DB
checkKM  Thread [15 ] is be notified
the km is 101,i will change DB
checkKM  Thread [14 ] is be notified
the km is 101,i will change DB
checkSite  Thread [13 ] is be notified
checkSite  Thread [12 ] is be notified
checkSite  Thread [11 ] is be notified
等待超时模式实现一个线程池

实现一个Connection,SqlConnectionImpl

public class SqlConnectionImpl implements Connection {

    /*拿一个数据库连接*/
    public static final Connection fetchConnection(){
        return new SqlConnectImpl();
    }

    @Override
    public Statement createStatement() throws SQLException {
        return null;
    }
    ...  
}

先写一个超时模式的连接池

public class DBPool {

    private LinkedList<Connection> pool = new LinkedList<>();


    public DBPool(int initialSize) {
        if(initialSize > 0){
            for (int i = 0;i<initialSize;i++){
                pool.add(SqlConnectImpl.fetchConnection());
            }
        }
    }


    public Connection fetchCon(long mills) throws InterruptedException {
        synchronized (pool){
            if(mills<0){
                while (pool.isEmpty()){
                    pool.wait();
                }
                return pool.removeFirst();
            }else{
                long overtime  = System.currentTimeMillis()+mills;
                long remain = mills;
                while (pool.isEmpty()&&remain>0){
                    pool.wait(remain);
                    remain = overtime - System.currentTimeMillis();
                }
                Connection result = null;
                if(!pool.isEmpty()){
                    result = pool.removeFirst();
                }
                return result;
            }

        }
    }

    public void releaseConn(Connection con){
        if(null!=con){
            synchronized (pool){
                pool.addLast(con);
                pool.notifyAll();
            }
        }
    }
}

然后定义一个测试

public class DBPoolTest {



    static DBPool dbPool = new DBPool(10);

    static CountDownLatch end;

    public static void main(String[] args ) throws InterruptedException {
        int threadCount = 50;
        end = new CountDownLatch(threadCount);

        int count = 20;

        AtomicInteger got = new AtomicInteger();
        AtomicInteger notGot = new AtomicInteger();

        for (int i = 0;i<threadCount;i++){
            Thread thread = new Thread(new Worker(count, got, notGot), "work_" + i);
            thread.start();

        }
        end.await();
        System.out.println("总共尝试了: " + (threadCount * count));
        System.out.println("拿到连接的次数:  " + got);
        System.out.println("没能连接的次数: " + notGot);

    }
    private static  class Worker implements Runnable{

        int count;
        AtomicInteger got;
        AtomicInteger notGot;

        public Worker(int count, AtomicInteger got, AtomicInteger notGot) {
            this.count = count;
            this.got = got;
            this.notGot = notGot;
        }

        @Override
        public void run() {
            while (count >0){
                try {
                    Connection connection = dbPool.fetchCon(1000);
                    if(null!=connection){
                        try{
                            connection.createStatement();
                            connection.commit();
                        }finally {
                            dbPool.releaseConn(connection);
                            got.incrementAndGet();
                        }
                    }else{
                        notGot.incrementAndGet();
                        System.out.println(Thread.currentThread().getName()+"等待超时");
                    }
                } catch (Exception e) {
                }finally {
                    count --;
                }

            }
            end.countDown();

        }
    }
}

输出结果如下:

work_30等待超时
work_21等待超时
work_26等待超时
...
work_6等待超时
work_21等待超时
work_20等待超时
work_40等待超时
总共尝试了: 1000
拿到连接的次数:  868
没能连接的次数: 132
join()

join()方法,线程A,执行了线程B的join(),线程A必须要等待B执行完成了以后,线程A才能继续自己的工作,可以用于嵌套定义线程的执行顺序,如下

public class UseJoin {

    private static class JumpQueue implements Runnable{
        private Thread thread;

        public JumpQueue(Thread thread) {
            this.thread = thread;
        }

        @Override
        public void run() {
            try {
                System.out.println(thread.getName()+"will join before "+Thread.currentThread().getName());
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+" terminted.");
        }
    }

    public static void main(String[] args){
        Thread previous = Thread.currentThread();
        for (int i = 0;i<10;i++){
            Thread thread = new Thread(new JumpQueue(previous), "work_" + i);
            System.out.println(previous.getName()+"will jump a queue the thread :"+thread.getName());
            thread.start();
            previous = thread;
        }
        SleepTools.second(2);
        System.out.println(Thread.currentThread().getName()+" terminted.");
    }
}

输出结果如下

mainwill jump a queue the thread :work_0
work_0will jump a queue the thread :work_1
mainwill join before work_0
work_1will jump a queue the thread :work_2
work_0will join before work_1
work_2will jump a queue the thread :work_3
work_1will join before work_2
work_3will jump a queue the thread :work_4
work_2will join before work_3
work_4will jump a queue the thread :work_5
work_3will join before work_4
work_5will jump a queue the thread :work_6
work_4will join before work_5
work_6will jump a queue the thread :work_7
work_5will join before work_6
work_7will jump a queue the thread :work_8
work_6will join before work_7
work_8will jump a queue the thread :work_9
work_7will join before work_8
work_8will join before work_9
main terminted.
work_0 terminted.
work_1 terminted.
work_2 terminted.
work_3 terminted.
work_4 terminted.
work_5 terminted.
work_6 terminted.
work_7 terminted.
work_8 terminted.
work_9 terminted.
调用yield() 、sleep()、wait()、notify()等方法对锁有何影响?
  • 线程在执行yield()以后,持有的锁是不释放的

  • sleep()方法被调用以后,持有的锁是不释放的,好的编码风格不要吧sleep(),写在同步块里面

    public class SleepLock {
    
        private Object lock = new Object();
    
        private  class ThreadSleep extends Thread {
            @Override
            public void run() {
                String name = Thread.currentThread().getName();
                System.out.println(name + "will take the lock");
                try {
                    synchronized (lock) {
                        System.out.println(name + " taking the lock ");
                        Thread.sleep(5000);
                        System.out.println(name + " finish the word ");
                    }
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        private  class ThreadNotSleep extends Thread{
            @Override
            public void run() {
                String name = Thread.currentThread().getName();
                System.out.println(name+"will take the lock "+System.currentTimeMillis());
                synchronized (lock){
                    System.out.println(name+" taking the lock  ");
                    System.out.println(name+" finish the work  "+System.currentTimeMillis());
                }
            }
        }
    
        public static void main(String[] args){
            SleepLock sleepLock = new SleepLock();
            Thread threadSleep = sleepLock.new ThreadSleep();
            threadSleep.setName("ThreadSleep");
            ThreadNotSleep threadNotSleep = sleepLock.new ThreadNotSleep();
            threadNotSleep.setName("ThreadNotSleep");
            threadSleep.start();
            try {
                Thread.sleep(1000);
                System.out.println("main slept");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            threadNotSleep.start();
        }
    }
    
    

    输出

    ThreadSleepwill take the lock
    ThreadSleep taking the lock 
    main slept
    ThreadNotSleepwill take the lock 1586845673063
    ThreadSleep finish the word 
    ThreadNotSleep taking the lock  
    ThreadNotSleep finish the work  1586845677063
    
  • 调动wait()方法之前,必须要持有锁。调用了wait()方法以后,锁就会被释放,当wait方法返回的时候,线程会重新持有锁

  • 调动notify()方法之前,必须要持有锁,调用notify()方法本身不会释放锁的