持续更新中,欢迎收藏,关注,以便查看后续
直接调用Run方法,程序中只有主线程这一个线程,执行路径只有一条,还是要顺序执行,需要run方法体执行完毕,才可执行下面的代码。(相当与普通的方法)
start方法启动线程,无需等待run方法体代码执行完毕,可以直接继续执行下面的代码。(真正的实现多线程)
创建一个MyThread方法继承Thread
public class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
public void run(){
for (int i = 0 ; i < 100 ; i++){
System.out.println(this.name +"--"+ i);
}
}
}
使用run方法:结果四个方法按照顺序运行,得出结论(直接使用run方法不是多线程)
public class demo {
public static void main(String[] args) {
MyThread my1 = new MyThread("第1个线程");
MyThread my2 = new MyThread("第2个线程");
MyThread my3 = new MyThread("第3个线程");
MyThread my4 = new MyThread("第4个线程");
my1.run();
my2.run();
my3.run();
my4.run();
}
}
使用start方法:结果发现四个方法交替输出,得出结论(start真正的实现多线程)
public class demo {
public static void main(String[] args) {
MyThread my1 = new MyThread("第1个线程");
MyThread my2 = new MyThread("第2个线程");
MyThread my3 = new MyThread("第3个线程");
MyThread my4 = new MyThread("第4个线程");
my1.start();
my2.start();
my3.start();
my4.start();
}
}
创建一个MyThread方法继承Thread
public class MyRunnable implements Runnable {
private String name;
public MyRunnable(String name) {
this.name = name;
}
public void run(){
for (int i = 0 ; i < 100 ; i++){
System.out.println(this.name +"--"+ i);
}
}
}
Runnable的代码实现
public static void main(String[] args) {
MyRunnable my1 = new MyRunnable("第1个线程");
MyRunnable my2 = new MyRunnable("第2个线程");
MyRunnable my3 = new MyRunnable("第3个线程");
MyRunnable my4 = new MyRunnable("第4个线程");
Thread t1 = new Thread(my1);
Thread t2 = new Thread(my2);
Thread t3 = new Thread(my3);
Thread t4 = new Thread(my4);
t1.start();
t2.start();
t3.start();
t4.start();
}
获取返回值示例
public class MyCallable implements Callable {
public String call() throws Exception {
return "hello world";
}
}
@Test
public void testThread1(){
// 1.获取FutureTask对象
MyCallable myCallable = new MyCallable();
FutureTask futureTask = new FutureTask(myCallable);
// 2.开启线程
new Thread(futureTask).start();
try{
String s = (String) futureTask.get();
System.out.println(s);
}catch (InterruptedException e){
e.printStackTrace();
}catch (ExecutionException e){
e.printStackTrace();
}
}
容量有限
基于数组的先进先出队列
BlockingQueue< Runnable > workQueue = new ArrayBlockingQueue<>(5);
容量无限
基于链表的先进先出队列
弊端:如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了
BlockingQueue< Runnable > workQueue = new LinkedBlockingQueue<>();
默认
队列满了之后它将抛出 RejectedExecutionException 异常
RejectedExecutionHandler rejected = new ThreadPoolExecutor.AbortPolicy();
队列满了丢任务不异常,但是线程池将丢弃被拒绝的任务。
RejectedExecutionHandler rejected = new ThreadPoolExecutor.DiscardPolicy();
将最早进入队列的任务删除,然后将被拒绝的任务添加到等待队列中。
RejectedExecutionHandler rejected = new ThreadPoolExecutor.DiscardOldestPolicy();
如果添加到线程池失败,那么主线程会自己去执行该任务
RejectedExecutionHandler rejected = new ThreadPoolExecutor.CallerRunsPolicy();
四种构造方法:
/**
* corePoolSize 核心线程数
* maximumPoolSize 最大线程数
* keepAliveTime idle线程存活时间
* unit 上个参数的单位
* workQueue 线程对象的缓冲队列
* threadFactory 生成线程的工厂
* handler 达到容量后的回调
*/
//1.
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
//2.
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
//3.
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
//4.
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
示例代码
public class Demo implements Runnable {
private static int sum = 0;
public void run() {
for (int i = 0;i<50000;i++) {
System.out.println(Thread.currentThread().getName() + ":" + (sum++));
}
}
public static void main(String[] args) {
Demo syncThread = new Demo();
Thread thread1 = new Thread(syncThread,"线程1");
Thread thread2 = new Thread(syncThread,"线程2");
Thread thread3 = new Thread(syncThread,"线程3");
Thread thread4 = new Thread(syncThread,"线程4");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
运行截图如下:多次运行结果不一致,这就是线程不安全。正常最后一个值应该为:50000*4-1=199999
示例代码
public class Demo implements Runnable {
private static int sum = 0;
public void run() {
synchronized (this) {
for (int i = 0;i<50000;i++) {
System.out.println(Thread.currentThread().getName() + ":" + (sum++));
}
}
}
public static void main(String[] args) {
Demo syncThread = new Demo();
Thread thread1 = new Thread(syncThread,"线程1");
Thread thread2 = new Thread(syncThread,"线程2");
Thread thread3 = new Thread(syncThread,"线程3");
Thread thread4 = new Thread(syncThread,"线程4");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
运行截图如下:最后一个值多次测试都为199999
示例代码:
public void run() {
synchronized (this) {
try {
//不仅仅让出CPU,还会让出已经占有的同步资源锁。
this.wait(1000);
for (int i = 0;i<50000;i++) {
System.out.println(Thread.currentThread().getName() + ":" + (sum++));
}
//只会让出CPU,不会导致锁行为的改变。
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
生活小案例:
notify:
好比你在上厕所,外面有很多人在等,但是有一个人是厕所管理员。等你出来的时候,由管理员随机找一个人去上厕所。
notifyAll:
好比你还在上厕所,外面依然有很多人在等待,但是没有厕所管理员。等你出来的时候,大家一起去抢厕所的使用权,当有一个人抢到厕所的时候,其他人重新等待空余的厕所。
第三步到第五步都属于阻塞状态
查看进程状态代码示例如下
public class Demo{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyRunnable());
System.out.println(thread.getState());//线程状态:初始(NEW)
thread.start();
System.out.println(thread.getState());//线程状态:运行(RUNNABLE)
//为了保证下方方法体执行完毕,让当前主线程休眠0.1s
Thread.sleep(100);
System.out.println(thread.getState());//线程状态:终止(terminated)
}
}
class MyRunnable implements Runnable{
public void run() {
for (int i = 0; i < 100; i++) {
}
System.out.println("循环完成~");
}
}
补充:
- 进入synchronized时,且没有获取到锁,线程状态 ---- blocked
直到锁被释放。线程状态 ---- runnable- 线程调用wait()或join时,线程状态 ---- waiting
调用notify或notifyAll时,或join的线程执行结束后,线程状态 ---- runnable- 线程调用sleep(time),或wait(time)时,线程状态 ---- timed waiting
当休眠时间结束后,或者调用notify或notifyAll时。线程状态 ---- runnable- 程序执行结束,线程状态 ---- terminated
使用yield线程会把CPU时间让掉,让所有线程在竞争一次,也就是说也就是谁先抢到谁执行。
代码示例如下:
public class Demo1 extends Thread {
private String name;
public Demo1(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(this.name+"-------"+i);
// 当i为5时,该线程就会把CPU时间让掉,让所有线程在竞争一次,也就是说也就是谁先抢到谁执行。
if (i == 5) {
this.yield();
}
}
}
public static void main(String[] args) {
Demo1 d1 = new Demo1("张三");
Demo1 d2 = new Demo1("李四");
Demo1 d3 = new Demo1("王五");
d1.start();
d2.start();
d3.start();
}
}
在多条线程访问的时候,我们在主程序中不需要去做任何的同步,的程序还能按照我们预期的行为去执行,那么我们就可以说这个类是线程安全的。
我们什么时候需要考虑线程安全呢?:多个线程访问同一个资源
· 如果是多个线程访问同一个资源,那么就需要上锁,才能保证数据的安全性。
· 如果每个线程访问的是各自的资源,那么就不需要考虑线程安全的问题,所以这个时候,我们可以放心使用非线程安全的对象
采用synchronized关键字给代码块或方法加锁
在java 5之后,java.util.concurrent.locks包下提供了另外一种方式来实现线程同步,就是Lock。