面试题:
JVM是多线程程序吗?至少有几条线程..
jvm是多线程的,
至少有2条线程...
有主线程,main..执行这些代码,能够被Jvm识别
垃圾回收线程
在执行程序的时候,一些对象会被Jvm释放掉,原因,
它开启了垃圾回收线程,里面GC:垃圾回收器(回收一些没有更多引用的对象或者变量...)
代码示例
public class Demo {
public static void main(String[] args) {
System.out.println("hello");
new Object();
new Object();
new Object();
new Object();
new Object();
// ......n个
System.out.println("world");
}
}
如何实现多线程程序呢?
要实现多线程程序,需要开启进程,
开启进程,是需要创建系统资源,但是Java语言不能创建系统资源
只有C/C++可以创建系统资源, 利用c语言创建好的系统资源实现
Java提供了一个类:Thread类
实现多线程程序的步骤:
1)将该类声明为Thread的子类
2)该子类应重写 Thread类的 run方法
3)在主线程创建该自定义线程类的对象
并行和并发(高并发:MyBatis --->IBatis:半自动化)
前者逻辑上的同时,指的是同一个时间段内
后者物理上的同时,指的是同一个时间点
代码示例
public class ThreadDemo {
public static void main(String[] args) {
// 创建MyThread类对象
// MyThread my = new MyThread() ;
// 当前Thread类有一个run public void run()
// my.run();
// System.out.println("-------------------");
// my.run();
// 执行线程不是run方法 ,run方法的调用相当于一个普通方法的调用
/*
* public void start()使该线程开始执行;Java虚拟机调用该线程的 run 方法。 结果是两个线程并发地运行
*/
MyThread t1 = new MyThread();
t1.start();
// IllegalThreadStateException:非法状态异常,同一个线程只能被执行一次
// t1.start();
MyThread t2 = new MyThread();
t2.start();
}
}
public final void setDaemon(boolean on) :true时,表示为守护线程将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。(守护线程不会立即结束掉,它会执行一段时间在结束掉)
该方法必须在启动线程前调用。
代码示例
package thread;
public class ThreadDemo2 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread myThread2 = new MyThread();
myThread.setName("张飞");
myThread2.setName("关羽");
myThread.setDaemon(true);// 设置为守护线程
myThread2.setDaemon(true);// 设置为守护线程
myThread.start();
myThread2.start();
Thread.currentThread().setName("刘备");
for (int x = 0; x < 5; x++)
System.out.println(Thread.currentThread().getName());
}
}
跟线程的优先级相关的方法:public final int getPriority()返回线程的优先级。
public final void setPriority(int newPriority)更改线程的优先级
线程存在一个默认优先级
public static final int MAX_PRIORITY 10 最大优先级
public static final int MIN_PRIORITY 1 最小优先级
public static final int NORM_PRIORITY 5 默认优先级
代码示例
package thread;
public class ThreadDemo3 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread myThread2 = new MyThread();
MyThread myThread3 = new MyThread();
myThread.setName("林青霞");
myThread2.setName("王祖贤");
myThread3.setName("林志颖");
myThread.setPriority(1);
myThread2.setPriority(5);
myThread3.setPriority(10);
System.out.println(myThread3.getPriority());
myThread2.start();
myThread.start();
myThread3.start();
}
}
public final void join():等待该线程终止 interruputedException 中断异常分别创建三个子线程,让第一个子线程执行之后,调用join()等待该线程终止,再执行t2,t3线程
代码示例
package thread;
public class ThreadDemo4 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread myThread2 = new MyThread();
MyThread myThread3 = new MyThread();
myThread.setName("李渊");
myThread2.setName("李世民");
myThread3.setName("李元霸");
myThread.start();
try {
myThread.join();
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
myThread2.start();
myThread3.start();
}
}
睡眠
Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,它
入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。
线程睡眠的原因:线程执行太快,或者需要强制进入下一轮,因为Java规范不保证合理的轮换。
睡眠的实现:调用静态方法。
try {
Thread.sleep(123);
} catch (InterruptedException e) {
e.printStackTrace();
}
睡眠的位置:为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程中会睡眠。
例如,在前面的例子中,将一个耗时的操作改为睡眠,以减慢线程的执行。可以这么写:
public void run() {
for(int i = 0;i<5;i++){
// 很耗时的操作,用来减慢线程的执行
// for(long k= 0; k <100000000;k++);
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace(); .
}
System.out.println(this.getName()+" :"+i);
}
}
public static void sleep(long millis):线程睡眠 指定是时间毫秒值
throws InterruptedException两个区别? public final void stop() ;强迫线程停止执行。 不会执行了 (过时了),方法能使用的
public void interrupt()中断线程。 表示中断线程的一种状态 面试题 区别? wait(): wait()调用的,立即释放锁 (同步锁/Lock锁)
sleep(): 线程睡眠,调用不会释放锁
代码示例
package thread;
import java.util.Date;
public class MyThread extends Thread {
public MyThread() {
super();
// TODO Auto-generated constructor stub
}
public MyThread(String name) {
super(name);
// TODO Auto-generated constructor stub
}
@Override
public void run() {
// TODO Auto-generated method stub
// super.run();
for (int x = 0; x < 50; x++) {
try {
Thread.sleep(1000);
System.out.println(getName() + "的线程睡眠1毫秒");
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
System.out.println(getName() + ":" + x);
}
}
}
package thread;
public class ThreadDemo4 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread myThread2 = new MyThread();
myThread.setName("李渊");
myThread2.setName("李世民");
myThread.start();
myThread2.start();
}
}
需求:某电影院出售某些电影票(复联3,红高粱....),有三个窗口同时进行售票(100张票),请您设计一个程序,模拟电影院售票两种方式:
继承
接口
第一种方式:继承类Thread
代码示例
package org.westos_01;
//SellTicket线程
public class SellTicket extends Thread {
// 为了不让外界更改这个类中的数据
// private
// 定一个票数
// 为了让每一个线程共同使用一个数据,此数据应该被static修饰
// private int tickets = 100 ;
private static int tickets = 100;
@Override
public void run() {
// 为了模拟电影卖票(模拟一直有票)
// 死循环
// st1,st2,st3都有执行这个里面的方法
// st1 100
// st2 100
// st3 100
while (true) {
if (tickets > 0) {
System.out.println(getName() + "正在出售第" + (tickets--) + "张票");
}
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
//创建三个子线程,分别代码三个窗口
SellTicket st1 = new SellTicket() ;
SellTicket st2 = new SellTicket() ;
SellTicket st3 = new SellTicket() ;
//设置线程名称
st1.setName("窗口1");
st2.setName("窗口2");
st3.setName("窗口3");
//启动线程
st1.start();
st2.start();
st3.start();
}
}
第二种方式 实现接口Runnable
代码示例
package org.westos_02;
public class SellTicket implements Runnable {
// 定义100张票
private int tickets = 100;
@Override
public void run() {
while (true) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
}
}
}
package org.westos_02;
//第二种方式实现
//给继续加一些内容(延迟操作实现)
public class SellTicketDemo {
public static void main(String[] args) {
// 创建资源类对象(共享资源类对象/目标对象(target))
SellTicket st = new SellTicket();
// 创建线程类对象
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
// 启动线程
t1.start();
t2.start();
t3.start();
}
}
为了模拟更真实的场景,加入延迟操作(让我们线程睡100毫秒)
程序的设计是好的,但是结果有一些问题
1)同一张票被卖了多次
CPU的执行有一个特点(具有原子性操作:最简单最基本的操作)
2)出现了0或者负票
(延迟操作+线程执行的随机性)
代码示例
package org.westos_03;
public class SellTicket implements Runnable {
//定义100张票
private int tickets = 100 ;
/* @Override
public void run() {
while(true) {
//t1先进来 t2
if(tickets>0) {
//为了模拟更真实的场景(网络售票有延迟的),稍作休息
try {
//t1睡 t2睡
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"正在出售第"+(tickets--)+"张票");
//100张票
//为什么会出现同一张票被卖多次?
*//**
* 出现同票的原因:CPU的执行有一个特点(具有原子性操作:最简单最基本的操作)
* t1线程进来,睡完了,100张票
* 原子性操作:记录以前的值
* 接着tickets-- :票变成99张票
* 在马上输出第99张票的时候,t2/t3进来,直接输出所记录的之前的票数
* 出现:
* 窗口1正在出售第100张票
* 窗口3正在出售第99张票
* 窗口2正在出售第99张票
* 原子性操作
}
}
}*/
@Override
public void run() {
while (true) {
try {
// t1睡 t2睡
Thread.sleep(100); // 延迟操作
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickets > 0) {
//t1,t2,t3 三个线程执行run里面代码
//为了模拟更真实的场景(网络售票有延迟的),稍作休息
System.out.println(Thread.currentThread().getName()
+"正在出售第"+(tickets--)+"张票");//0
/**
* 理想状态:
* t1正在出售第3张票
* t3正在出售第2张票
* t2正在出售第1张票
* ...
* 负票
* t1出售第0张票 (延迟操作+线程执行的随机性)
* t3正在出售-1张票
*
*/
}
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
// 创建资源类对象(共享资源类/目标对象)
SellTicket st = new SellTicket();
// 创建线程类对象
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
// 启动线程
t1.start();
t2.start();
t3.start();
}
}
通过刚才的这个程序,有安全问题(同票还有负票)如何解决多线程的安全问题?
校验一个多线程程序是否有安全隐患的前提条件:
1)当前程序是多线程环境
2)有共享数据
3)有多条语句对共享数据进行操作
看当前案例是否有多线程的安全问题:
1)是否是多线程环境 是
2)是否有共享数据 是
3)是否有多条语句对共享数据进行操作 是
现在就需要解决安全问题:
1)多线程环境 不能解决
2)对共享数据进行优化 不能解决
3)在多条语句对共享数据进行操作这一环进行解决
解决方案:就是将多条语句对共享数据操作的代码,用同步代码块包起来
格式:
synchronized(锁对象){
针对多条语句对共享数据操作代码;
}
锁对象:肯定一个对象,随便创建一个对象(匿名对象)
给刚才的这个程序加入了同步代码块,但是锁对象使用的是匿名对象(每一个线程进来都有自己各自的锁),还是没有解决!
锁对象:所有线程公用一把锁
代码示例
package demo;
public class SellTicket implements Runnable {
private int tickets = 100;
private Object object = new Object();
@Override
public void run() {
while (true) {
synchronized (object) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
}
}
}
}
package demo;
public class SellTicketDemo {
public static void main(String[] args) {
// 创建资源类对象(共享资源类对象/目标对象(target))
SellTicket sellTicket = new SellTicket();
// 创建线程类对象
Thread thread = new Thread(sellTicket, "窗口1");
Thread thread2 = new Thread(sellTicket, "窗口2");
Thread thread3 = new Thread(sellTicket, "窗口3");
// 启动线程
thread.start();
thread2.start();
thread3.start();
}
}
举例:火车上上厕所(锁对象:将它看成是门的开和关)
synchronized(锁对象){
多条语句对共享数据进行操作的代码;
}
注意:
锁对象:一定要使用同一把锁(所有线程只能公用一把锁)
锁对象:任何的Java类(引用类型)
代码示例
package org.westos_05;
public class SellTicket implements Runnable {
// 定义100张票
private int tickets = 100;
private Object obj = new Object();
private Demo d = new Demo();
@Override
public void run() {
while (true) {
// t1,t2,t3
synchronized (d) { // 门的开和关
// t1进来,门会关掉
// t2进来,门关掉
// t3进来,门关掉
if (tickets > 0) {
try {
// 0.1
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
// 窗口1正在出售第100张票
// 窗口2正在出售第99张票
// 窗口3正在出售98张票
// ....
// 虽然加入延迟操作,但是因为有synchronized,所以不会出现0票或者负票了
}
}
}
}
}
class Demo {
}
package org.westos_05;
/**
* 举例:
* 火车上上厕所(锁对象:将它看成是门的开和关)
*
* synchronized(锁对象){
* 多条语句对共享数据进行操作的代码;
* }
*
*注意:
* 锁对象:一定要使用同一把锁(所有线程只能公用一把锁)
* 锁对象:任何的Java类(引用类型)
*
*
*/
public class SellTicketDemo {
public static void main(String[] args) {
//创建资源类对象(共享资源类对象/目标对象)
SellTicket st = new SellTicket() ;
//创建线程类对象
Thread t1 = new Thread(st, "窗口1") ;
Thread t2 = new Thread(st ,"窗口2") ;
Thread t3 = new Thread(st, "窗口3") ;
//启动线程
t1.start();
t2.start();
t3.start();
}
}
同步方法
当同步方法为静态(static)时
private synchronized static void SellTicket(){}
synchronized(类名.class);
当同步方法为非静态时
private synchronized void SellTicker(){}
synchronized(this);
代码示例
package demo;
public class SellTicket implements Runnable {
private static int tickets = 100;
private static int x = 100;
@Override
public void run() {
while (true) {
if (x % 2 == 0) {
synchronized (this) {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"+" x:"+x--);
}
}
}
else {
sellTicket();
}
}
}
//静态同步方法
/*private synchronized static void sellTicket() {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"+" x:"+x--);
}
}*/
//非静态同步方法
private synchronized void sellTicket() {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"+" x:"+x--);
}
}
}
线程安全的类 StringBuffer
线程安全的集合
单列集合 Vector
双列集合 Hashtable
代码示例
package org.westos_06;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
public class ThreadDemo {
public static void main(String[] args) {
// 线程安全的类StringBuffer Vector<V> Hashtable<K,V>
StringBuffer sb = new StringBuffer();
Vector<String> vector = new Vector<String>();
Hashtable<String, String> hm = new Hashtable<String, String>();
// Vector<String>它是线程安全的集合,但是还是不习惯使用这个集合,通过ArrayList集合:线程不安全的类
List<String> arrayList = new ArrayList<String>(); // 线程不安全的类
ArrayList<String> arrayList2 = new ArrayList<String>();
// public static <T> List<T> synchronizedList(List<T> list)
// 返回指定列表支持的同步(线程安全的)列表
List list = Collections.synchronizedList(arrayList); // 线程安全的方法
List list2 = Collections.synchronizedList(arrayList2); // 线程安全的方法
}
}j
jdk5.0以后,java提供了一个具体的锁: 接口:Lock
private Lock lock=new ReentrantLock();
lock.lock();//显示获取锁
lock.unlock();//释放锁
代码示例
package org.westos_07;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SellTicket implements Runnable {
// 定义票
private int tickets = 100;
// Object obj = new Object();
// Jdk5.0以后,java提供了一个具体的锁: 接口:Lock
private Lock lock = new ReentrantLock(); // 显示获取锁的前提,一定要创建Lock接口对象
@Override
public void run() {
while (true) {
try { // try...finally
lock.lock(); // 获取锁 syncrhonized(obj)
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
} finally {// 释放锁
if (lock != null) {
lock.unlock();
}
}
}
}
}
死锁
简单描述就是比如说有两个锁,第一个锁中包含着第二个锁,第二个锁中也包含着第一个锁,第一个锁执行到其代码块中有第二个锁的时候,它会等第二个锁执行完毕释放掉后才能执行此处的代码,必须等待第二个锁执行完毕,但是线程抢到CPU主动权后执行第二把锁对应的代码块时,执行到其代码块中有第一把锁的地方,必须等第一把锁执行完毕释放掉第一把锁后才会执行此处代码,所以第二把锁在等待第一把锁执行完毕,就这样,第一把锁在等待第二把锁,第二把锁也在等待第一把锁,互相等待啊!!!,就这样一直等啊等,等啊等,就一直不执行,一直等着,就变成了死锁。
代码示例
package org.westos_08;
public class DieLock extends Thread {
// 声明一个成员变量
private boolean flag;
public DieLock(boolean flag) {
this.flag = flag;
}
// 重写run方法
@Override
public void run() {
if (flag) {
synchronized (MyLock.objA) {
System.out.println("if ObjA");
synchronized (MyLock.objB) {
System.out.println("if objB");
}
}
} else {
synchronized (MyLock.objB) {
System.out.println("else objB");
synchronized (MyLock.objA) {
System.out.println("else objA");
}
}
}
}
/**
* 第一种情况: if ObjA else objB
*
* 第二种情况 else objB if ObjA
*
* 第三种情况: 理想状态 else objB else objA if ObjA if objB
*
* if ObjA if objB else objB else objA
*
*
*/
}
package org.westos_08;
/**
* 自定义两个锁对象
*
*/
public class MyLock {
// 两个锁对象分别是objA 和objB
public static final Object objA = new Object();
public static final Object objB = new Object();
}
package org.westos_08;
//测试类
//解决了多线程安全问题,但是还是有些问题:
//1)执行效率低
//2)会产生死锁
//两个或两个以上的线程,在执行的过程中出现互相等待的情况,就叫做死锁!
public class DieLockDemo {
public static void main(String[] args) {
// 创建线程了对象
DieLock dl1 = new DieLock(true);
DieLock dl2 = new DieLock(false);
// 启动线程
dl1.start();
dl2.start();
}
}
分析:Student类: 资源类
SetThread:设置学生的数据(生产者线程)
GetThread:获取(输出)学生数据(消费者线程)
StudentDemo:测试类
需求:SetThread线程给学生对象进行赋值,再通过消费者线程输出该学生数据,设计这样一个程序!
null----0 按照刚才的思路,发现有一个问题,的数据null---0
解决方案:线程死锁的注意事项:要保证生产者线程和消费者线程是针对同一个对象进行操作的!
在主线程main中创建一个学生对象,将这个学生对象通过构造方法传给每一个线程
代码示例
package org.westos_09;
public class Student {
String name;
int age;
}
package org.westos_09;
//生产者线程
public class SetThread implements Runnable {
private Student student;
public SetThread(Student s) {
this.student = s;
}
@Override
public void run() {
// 设置学生数据
student.name = "高圆圆";
student.age = 27;
}
}
package org.westos_09;
//消费者线程
public class GetThread implements Runnable {
private Student student;
public GetThread(Student s) {
this.student = s;
}
@Override
public void run() {
// 输出该学生数据
System.out.println(student.name + "----" + student.age);
}
}
public class StudentDemo {
public static void main(String[] args) {
// 针对同一个对象进行操作
Student s = new Student();
// 创建线程类对象
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
// 创建线程了对象
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);
// 启动线程
t1.start();
t2.start();
}
}
分析:
Student类: 资源类
SetThread:设置学生的数据(生产者线程)
GetThread:获取(输出)学生数据(消费者线程)
StudentDemo:测试类
需求:SetThread线程给学生对象进行赋值,在通过消费者线程输出该学生数据,设计这样一个程序!
null----0 按照刚才的思路,发现有一个问题,的数据null---0
解决方案:线程死锁的注意事项:要保证生产者线程和消费者线程针对同一个对象进行操作的!
在外部创建一个学生对象,将这个学生对象通过构造方法传入到各个线程中
需求:消费者线程,和生产者线程加入循环操作,改进
又有问题:
1)同一个人的姓名和年龄出现多次
2)姓名和年龄不符
为什么?
1)CPU的一点点时间片,在某一个时间点,足够它执行很多次
2)线程具有随机性
解决方案:
1)是否是多线程环境 是
2)是否有共享数据 是
3)是否有多条语句对共享数据进行操作 有
同步机制(同步代码块/同步方法)
开发中,使用synchronized(Lock锁也可以)同步代码块将多条语句对共享数据的操作包起来!
代码示例
package org.westos_10;
//生产者线程
public class SetThread implements Runnable {
private Student s;
// 定义一个变量
private int x = 0;
public SetThread(Student s) {
this.s = s;
}
@Override
public void run() {
while (true) {
synchronized (s) {
if (x % 2 == 0) {
s.name = "高圆圆";
s.age = 27;
} else {
s.name = "张杨";
s.age = 28;
}
x++;
}
}
}
}
package org.westos_11;
//消费者线程
public class GetThread implements Runnable {
private Student s;
public GetThread(Student s) {
this.s = s;
}
@Override
public void run() {
// 输出该学生数据
while (true) {
synchronized (s) {
// 如果本身消费者有数据
if (!s.flag) { // true==!false s.flag==false 说明没有数据 !flag
try {
s.wait();// 和网络编程里面的accept()一样 都属于阻塞式方法
// 消费线程等待,等待生产者线程生产数据
} catch (InterruptedException e) {
e.printStackTrace();
}
} // !s.flag==false s.flag==true 说明有数据,有数据则输出数据
System.out.println(s.name + "----" + s.age);// 高圆圆---27
// 输出之后就没有数据了
s.flag = flase;
// 通知生产者线程,让其赶紧生产数据
s.notify(); // 唤醒生产者线程
}
}
}
}
package org.westos_11;
//消费者线程
public class GetThread implements Runnable {
private Student s;
public GetThread(Student s) {
this.s = s;
}
@Override
public void run() {
// 输出该学生数据
while (true) {
synchronized (s) {
// 如果本身消费者有数据
if (!s.flag) { // true==!false s.flag==false 说明没有数据 !flag
try {
s.wait();// 和网络编程里面的accept()一样 都属于阻塞式方法
// 消费线程等待,等待生产者线程生产数据
} catch (InterruptedException e) {
e.printStackTrace();
}
} // !s.flag==false s.flag==true 说明有数据,有数据则输出数据
System.out.println(s.name + "----" + s.age);// 高圆圆---27
// 输出之后就没有数据了
s.flag = false;
// 通知生产者线程,让其赶紧生产数据
s.notify(); // 唤醒单生产者线程
}
}
}
}
public class StudentDemo {
public static void main(String[] args) {
// 针对同一个对象进行操作
Student s = new Student();
// 创建线程类对象
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
// 创建线程类对象
Thread t1 = new Thread(st); // 生产者
Thread t2 = new Thread(gt);// 消费者
// 启动线程
t1.start();
t2.start();
}
}
ThreadGroup 线程组
线程组:表示一个线程的集合。此外,线程组也可以包含其他线程组
代码示例
package org.westos_12;
/**
* 线程组:表示一个线程的集合。此外,线程组也可以包含其他线程组
*
* 线程池(某个线程执行完毕,反复利用线程对象)
*
*/
public class ThreadGroupDemo {
public static void main(String[] args) {
// 获取线程组的名称
//
// method1();
// 如何给多个线程设置一个线程组名称呢?
method2();
}
private static void method2() {
// public ThreadGroup(String name)构造一个新线程组
ThreadGroup tg = new ThreadGroup("main-新的线程组");
MyThread my = new MyThread();
// Thread(ThreadGroup group, Runnable target, String name)
Thread t1 = new Thread(tg, my, "线程1");
Thread t2 = new Thread(tg, my, "线程2");
// 直接获取线程组名称
System.out.println(t1.getThreadGroup().getName());
System.out.println(t2.getThreadGroup().getName());
}
private static void method1() {
MyThread my = new MyThread();
// 创建线程类对象
Thread t1 = new Thread(my, "线程1");
Thread t2 = new Thread(my, "线程2");
// public final ThreadGroup getThreadGroup()返回该线程所属的线程组
ThreadGroup tg1 = t1.getThreadGroup();
ThreadGroup tg2 = t2.getThreadGroup();
// public final String getName():返回线程组的名称
System.out.println(tg1.getName()); // main
System.out.println(tg2.getName());// main
// 所有的线程它默认的线程组名称:main(主线程)
System.out.println(Thread.currentThread().getThreadGroup().getName());// main
}
}
package org.westos_12;
public class MyThread implements Runnable {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}
线程池 ExecutorService
线程池:多个线程执行完毕,它会重新回到线程池中,等待被利用,不会变成垃圾!
和线程池有关的类
类 Executors: 一种工厂类
方法:
和线程池的创建有关系
public static ExecutorService newFixedThreadPool(int nThreads)
创建一个可重用固定线程数的线程池
ExecutorService:可以执行异步任务
创建一个线程池,执行接口中的方法
提交:Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future
Future:接口
Future 表示异步计算的结果
线程池调用完毕可以关闭的
void shutdown():关闭之前,会提交刚才的任务
代码示例
public class ExceutorsDemo {
public static void main(String[] args) {
// 创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(2);// 创建一个线程池中包含了2条线程
// 提交和Runnable接口的方法或者Callable(提交任务)
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
// pool-1-thread-2 :线程池-池数-线程类对象的描述-编号(从1开始)
// 关闭线程池
pool.shutdown();
}
}
package org.westos_13;
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}
Callable
call()方法
多线程第三种实现方式:
前提:自定义类实现Callable接口
1)创建线程池对象: Executors 里面的那个方法,返回的是ExecutorsService
2) 然后调用ExecutorsService里面的提交任务的方法:
<T> Future<T> submit(Callable<T> task)提交一个返回值的任务用于执行
3)关闭线程池
代码示例
public class ExecutorsDemo {
public static void main(String[] args) {
// 创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
// 提交任务
pool.submit(new MyCallable());
pool.submit(new MyCallable());
// 关闭线程池
pool.shutdown();
}
}
package org.westos_14;
import java.util.concurrent.Callable;
//Callable的泛型的类型它是call方法的返回值类型
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
for (int x = 0; x < 100; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
return null;
}
}
需求:利用线程来计算数字之和
代码示例
package org.westos_15;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 分别计算每个线程的求和!
*
*/
public class ExecutorsTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
// 提交任务
Future<Integer> f1 = pool.submit(new MyCallable(100));
Future<Integer> f2 = pool.submit(new MyCallable(200));
// V get():获取结果
Integer i1 = f1.get();
Integer i2 = f2.get();
System.out.println(i1);
System.out.println(i2);
// 关闭线程池
pool.shutdown();
}
}
package org.westos_15;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
// 定义个变量
private int number;
public MyCallable(int number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
// 定义最终结果变量
int sum = 0;
for (int x = 1; x <= number; x++) {
sum += x;
}
return sum;
}
}