2020.12.08:学习笔记-高并发编程(一)

线程的概念

一个程序中不同的执行路径,可以放在不同的CPU里边同步执行的

如何启动一个线程

  • 一、继承Thread类 (Java.lang.Thread)
    Java.lang.Thread 是应用程序层面对Java线程的抽象类。也就是说,一个Thread类代表一个Java线程。
    可以通过重写Thread的run()方法规定线程要完成的任务。
    可以通过调用 Thread的 start() 方法启动一个线程。
    示例:
public class Main {
    public static void main(String[] args) {
        new TestThread().start();
        System.out.println("Main ");
    }
}

class TestThread extends Thread{
    @Override
    public void run() {
        try {
            //Old style
            //Thread.sleep(1000);
            //让线程暂停一秒,被注释的是JDK 1.5之前的写法。
            TimeUnit.MILLISECONDS.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("thread is running");
    }
}

运行结果:

Main 
thread is running

可以看到,thread is running在程序运行一秒后被打印在控制台上,两个线程(main线程 和 TestThread创建的线程)没有相互影响。
注意:
通过调用 Threadstart() 方法启动一个线程。而不是run()方法,调用run()方法如同普通类的public方法调用。

  • 二、实现Runnable接口(Java.lang.Runnable)
    通过Thread启动线程并不是一种理想的方式,缺点如下:
    由于Java的单继承限制,继承Thread类阻止了我们继承其他类。
    更加符合面向对象的规则,Runnable被视作“任务”,Thread则是Java程序中实现并发的“工具”。
    我们可以通过Thead类的构造方法:
    Thread(Runnable runnable)构建一个线程。
    上一段代码的等效代码:
public class Main {
    public static void main(String[] args) {
        Runnable myTask = new TestTask();
        new Thread(myTask).start();
        System.out.println("Main ");
    }
}

class TestTask implements Runnable{
    @Override
    public void run() {
        try {
            //Old style
            //Thread.sleep(1000);
            TimeUnit.MILLISECONDS.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("thread is running");
    }
}
  • 三、匿名内部类:
    匿名内部类本质上也是一个类实现了Runnable接口,重写了run()方法,只不过这个类没有名字,直接作为参数传入Thread类,示例代码:
    Main类:
public class Main {
	
	public static void main(String[] args) {
		new Thread(new Runnable() {
			public void run() {
				for (int i = 0; i < 50; i++) {
					System.out.println(Thread.currentThread().getName() + "线程执行" + i);
				}
			}
		}).start();
		for (int i = 0; i < 50; i++) {
			System.out.println(Thread.currentThread().getName() + "线程执行" + i);
		}
	}
}

Further Questions:

  1. Thread 类自身维护了一个 对于其本身的引用 所以直到run()方法执行完成,Thread实例不会被垃圾回收器回收。
  2. run()方法无法向外抛出任何异常,所以我们应该认真设计线程中的异常处理、流程控制、线程结束后的资源处理。
  3. 由于多个线程同时运行,两个线程“同时”访问或修改同一资源的事件可能时常发生,我们需要极其认真处理这类事件。(synchronized volatile以及lock)
    严格来说,单处理器机器上,两个线程不可能“同时”访问一个资源。然而在线程看来,每个线程都是在独享CPU,这种方式的底层实现是CPU时间切片,我们永远无法确定线程在何使被“挂起”,在何时运行。

基本的线程同步:synchronized

synchronized 对某个对象加锁

	`synchronized`锁的是对象而不是下边的代码块。一个`synchronized`相当于一个原子操作,原子是不可分的,
	在线程执行这个代码块的时候是不肯能被打断的,执行完成之后其他线程才能够继续执行同一块代码。
  • 示例一:
public class T{
	private int count = 10;
	//o对象创建在堆内存中
	private Object o = new Object();

    public void m() {
        //任何线程执行下边的代码都要拿到o这把锁,该o对象是从堆内存中取出的,不是对象的引用
        synchronized (o) {
            /*
             *如果第一个线程运行到此处,第二个线程进入的话,需要第一个线程释放o
             *这个锁对象,第二个线程才可以执行;
             *只要有一个线程拿到了这把锁其他线程就拿不到了,这样的锁叫互斥锁。
             */
            count--;
            System.out.println(Thread.currentThread().getName() + "count = " + count);
        }
    }
}
  • 示例二:
public class T {
    private int count = 10;
    public void m() {
        //任何线程执行下边的代码都要拿到this这把锁
        synchronized (this) {
        	/*
       		 *使用this对象作为锁的话,new这个对象的时候会得到这把锁
      		 */
            count--;
            System.out.println(Thread.currentThread().getName() + "count = " + count);
        }
    }

}
public class T {
    private int count = 10;
    /**
     *如果一段代码在开始的时候就使用了synchronized (this)直到结束,
     *这个时候synchronized 可以直接写在方法的声明上,
     *等同于synchronized (this)的写法。
     */
    public synchronized void m() {
    		//这样的写法不是锁定当前的代码,而是要执行代码的时候锁定当前对象
            count--;
            System.out.println(Thread.currentThread().getName() + "count = " + count);
    }

}
  • 示例三
public class M {
    private static int count = 10;
	 /**
      * 如果用在静态方法上相当于锁定的是M.class这个类的对象
      * 静态的方法静态的属性是不需要new出对象就可以访问的,所以这里没有this
      * */
    public synchronized static void m() {
        //这里等同于synchronized (M.class)
        count--;
        System.out.println(Thread.currentThread().getName() + "count = " + count);

    }

    public static void n() {
        synchronized (T.class) {
            count--;
            System.out.println(Thread.currentThread().getName() + "count = " + count);
        }
    }
}
  • 示例四
public class T implements Runnable {
    private static int count = 10;

    @Override
    public  /*synchronized*/ void run() {
        //线程重入问题 第一个线程方法未执行完毕 第二个线程进入 他们在堆内存中公用的是一个T对象
        count--;
        System.out.println(Thread.currentThread().getName() + "count = " + count);
    }

    public static void main(String[] args) {
        T t = new T();
        for (int i = 0; i < 5; i++) {
            new Thread(t, "THREAD" + i).start();
        }
    }
}

运行结果:

THREAD0count = 9
THREAD3count = 7
THREAD1count = 8
THREAD4count = 7
THREAD2count = 7

如果想得到运行结果是正确顺序的结果的话 在方法执行的时候加把锁。/*synchronized*/加在方法上边。

每次运行结果:

THREAD0count = 9
THREAD1count = 8
THREAD2count = 7
THREAD4count = 6
THREAD3count = 5
  • 同步方法和非同步方法能否同时执行?
public class Sync {

    public synchronized void m1() {
        System.out.println(Thread.currentThread().getName() + "m1 start...");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "m1 end...");
    }

    public void m2() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "m2 ...");
    }

    public static void main(String[] args) {
        Sync t = new Sync();

        new Thread(() -> t.m1(), "t1:").start();
        new Thread(() -> t.m2(), "t2:").start();

        /*new Thread(t::m1, "t1").start();
        new Thread(t::m2, "t2").start();*/
    }
}

运行结果:在同步(synchronized)方法在执行的过程中非同步方法是可以执行的。只有synchronized方法在执行的过程中需要申请这把锁,而别的方法是不需要申请的。

t1:m1 start...
t2:m2 ...
t1:m1 end...
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值