快捷键:ALT+enter 创建对象
参数列表·ctr P
查看构造器:ctrl o
一、多线程
1.概念
单线程:就是一条线内能执行的程序
每一份线程有单独的:虚拟机栈 和程序计数器
多个线程共享:方法区和堆 一个进程一份,
2.单核CPU:
只能执行一个线程
多核CPU:
java应用程序java.exe,至少有三个线程:main()主线程,gc()垃圾回收站,异常处理线程
注意:
在单核CPU下, 只使用单个线程先后完成多个任务,比多个线程完成用的时间短
3.并行与并发
并行:多个CPU同时执行多个任务,不同人做不同事
并发:一个CPU同时执行多个任务
4.关于线程的8个方法
/**1.Thread.currentThread():静态方法 返回执行当前代码的线程
* 2.getName():获取当前线程的名字 ①构造器命名public thread(String name){}②通过当前对象命名
* 3.setName():设置当前线程的名字
* 4.yield():释放当前执行得线程,给别的线程用
* 5.join():在线程a中调用线程b的join(),此时线程a进入阻塞状态,直到join()的线程完全执行完以后,线程a才能继续执行
* 6.stop():过时 强制结束当前线程
* 7.sleep(long millitime): 让当前线程睡眠指定几毫秒 抛异常
* 8.isAlive():判断当前线程是否存活
5.线程的调用
时间片
抢占式:高优先级的线程抢占CPU
6.线程的优先级
线程的优先级 不一定实施
MAX_PRIORITY 优先级10
NORM_PRIORITY 5
MIN_PRIORITY 1
创建多线程的方式
一、继承Thread()
- 当前类继承Thread类
- 再启动一个线程,必须创建一个新的Thread子类的对象H1,调用此对象的start()
1.创建一个继承于Thread类的子类
* 2.重写Thread类的run 将此线程执行的内容声明在run()方法中
* 3.创建Thread类子类的对象 主线程
* 4.通过此对象调用start()方法
*
// 编译100以内的偶数
class HelloThread extends Thread{ //1.
public void run() { //2.
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority()+ ":" + i);
}
}
}
public HelloThread(String name){ //有参构造器
super(name);
}
}
public class ThreadMethodTest { public static void main(String[] args) { //分线程 HelloThread H1=new HelloThread("Thread:1"); //3.方式一 有参构造器
H1.setPriority(Thread.MAX_PRIORITY); //最小值10
H1.start(); //4.
Thread.currentThread().setName("主线程"); //主线程 Thread.currentThread().setPriority(Thread.MIN_PRIORITY); // 1
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName()+Thread.currentThread().getPriority()+ ":" + i);
}
}
}
}
二、实现runnable接口
1.创建一个实现了runnable接口的类
2.类去实现runnable接中的抽象方法
3.创建实现类的对象
4.将此对象作为参数传递到Thread类中的构造器中,创建thread类的对象 多态
5.通过thread类的对象调用start()
源码:
class Mthread implements Runnable{ //继承于object
public void run(){
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(i);
}
}
}
public static class ThreadTest1 {
public static void main(String[] args) {
Mthread Mthread1=new Mthread(); //创建当前类的对象
Thread t1=new Thread( Mthread1);
//对象作为参数传递到Thread类中的构造器中,创建thread类的对象 多态
t1.start();
//②调用了当前线程的run() 调用了private runnable target
}
}
}
问题:创建线程,继承和接口如何选择
优先选择实现Runnable接口
继承父类
共享数据
7.线程的生命周期
①新建:一个thread类或子类对象被声明创建时,新生的线程对象处于新建的状态
②就绪:新建状态被start()后,进入线程队列等待CPU时间片
③阻塞:被人挂起或执行输入输出操作时,让出CPU并临时中止自己的执行(sleep(long time),join,wait(),suspend() )
④运行:线程被调度,run()方法定义了线程的功能(运行就绪可反复执行)
⑤死亡:完成了全部工作或线程提前强制性中止或出现异常导致结束(执行完run()、stop、Error、Exception)
线程的同步 /线程的安全问题
线程的安全问题:例如,一个线程执行卖票过程中未完成,另一个线程也参与进来,操作卖票
同步机制:
一、同步代码块
- 可以继承Thread类
- 可以实现Runnable接口
二、同步方法
操作的共享数据的代码完整的声明在一个方法中,我们将此方法声明为同步的
3.也可以继承Thread类
4.也可以实现Runnable接口
在继承下,每个对象都有属性,如果要共享,给属性加上static
static Object obj=new Object(); //设置为静态对象 共用同一把锁
继承Thread类
同步方法的同步监视器 不需要显示的声明
非静态的方法 同步监视器是this
非静态的同步方法:同步监视器是当前类本身
**线程安全问题
* 一、同步代码块解决Runnable接口线程安全
* synchronized(同步监视器){
* 被同步的代码,即操作共享数据的代码
* }
* 说明:
* 共享数据:多个线程共同操作的变量 例如 ticket
* 同步监视器:即锁 任何一个类的对象都可以充当锁 多个线程共用一把锁!!
* 相当于单线程
//三个线程共用100张票
//方法一 :同步代码块 解决Runnable接口线程安全
class window1 implements Runnable{
private int ticket=100; //三个线程共用100张票
Person p1=new Person();
public void run(){
while(true){
synchronized (p1){ //包住和ticket相关的代码块
if(ticket>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票"+ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class WindowTest1 {
public static void main(String[] args) {
window1 w1=new window1();
Thread t1=new Thread(w1); //多态 父类为thread
Thread t2=new Thread(w1);
Thread t3=new Thread(w1);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Person{ } //定义在其他类
补充:
- 在实现Runnbale接口时创建多线程的方式中,考虑使用this充当同步监视器
- 继承Thread类创建多线程,慎用this充当同步监视器,用当前类作为同步监视器
//方法二 同步方法 实现runnbale接口
操作的共享数据的代码完整的声明在一个方法中,我们将此方法声明为同步的
class window3 implements Runnable{
private int ticket=100; //三个线程共用100张票
public void run(){
while(true){
show();
}
}
public synchronized void show(){
//同步监视器:this
if(ticket>0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName() + "卖票" + ticket);
ticket--;
}}}}
public class WindowTest3 {
public static void main(String[] args) {
window1 w1=new window1();
Thread t1=new Thread(w1); //多态
Thread t2=new Thread(w1);
Thread t3=new Thread(w1);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
三、!Lock锁
JDK5.0新增
- *Lock锁:*显示锁手动提供同步(lock()),手动解锁
- *synchronized():*隐式锁 自动释放同步监视器
class Window implements Runnable {
private int ticket=100;
private ReentrantLock lock=new ReentrantLock(true); //创建锁对象
public void run(){
while(true){
try{
lock.lock(); //调用锁定方法
if(ticket>0){
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票窗口为"+ticket);
ticket--;
}else {
break;
}
}finally{ //一定会执行的代码
lock.unlock(); //调用解锁方法
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window w=new Window();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
四种方式使用的优先顺序
Lock锁------>同步代码块-------->同步方法(在方法体之外)
10.死锁:
不同线程分别占用对方所需的资源不放手,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁,所有线程处于阻塞状态
一个先A后B 一个先B后A
11.线程的通信:
wait():执行后阻塞,当前线程进入阻塞状态,并释放同步监视器
notify():(唤醒)一旦执行此方法后,就会唤醒优先级高的一个线程,
notifyAll():唤醒所有被wait的线程
三个方法的调用者是同步代码块或同步方法中的同步监视器,否则会出现IllegalMonitorStateException异常
三个方法在Java.lang.Object中
//线程通信 线程1 线程2交替打印 1-100
class Number implements Runnable{
private int number=1;
public void run(){
while(true){
synchronized(this){ //同步监视器 任何一个类的对象可以充当
notify(); //线程2 唤醒线程1
if(number<=100){
System.out.println(Thread.currentThread().getName()+":"+number);
number++;
try {
wait(); //阻塞一下!抛异常 线程2执行完wait后释放同步监视器
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public class Communicate {
public static void main(String[] args) {
Number n1=new Number(); //创建当前类的对象
Thread t1 = new Thread(n1);//多态 父类给子类
Thread t2 = new Thread(n1);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
sleep()和wait()的异同
都导致进入阻塞
不同:
- 声明位置不同:Thread类中声明sleep(),Object中声明wait()
- wait()在同步代码块或同步方法中使用
- 是否释放同步监视器 :sleep()不会释放锁 ,wait()会释放锁
三、Callable接口 JDK5.0
如何理解Callable接口与Runnable接口创建线程方式强大
- 与run()方法相比,可以有返回值
- 方法可以抛出异常,被外面的操作捕获,获取异常类的信息
- 支持泛型的返回值
* 实现callable接口 JDK5.0
* 1.创建类实现call方法
* 2.创建接口实现类对象
* 3.将实现类对象的参数传递到FutureTask构造器中,创建FutureTask的对象
* 4.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
* 5.获取callable中call()的返回值
// 打印出1-100的偶数
class newThread implements Callable { //1.创建一个实现Callable接口的类
public Object call() throws Exception{ //回调方法
int sum=0; //装箱
for(int i=0;i<=100;i++){
if(i%2==0){
System.out.println(i);
sum+=i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
newThread newThread = new newThread(); //2.创建callable接口实现类对象
FutureTask futureTask = new FutureTask(newThread);
// 泛型 3.将callable接口实现类对象传递到FutureTask构造器中,创建FutureTask对象
new Thread(futureTask).start(); // FutureTask继承了runnable接口
//4.将FutureTask的对象作为 参数传递到Thread类的构造器中,并调用start()方法
try{
//5.get()返回值即为FutureTask构造器参数 callable实现类重写的call方法的返回值
Object sum = futureTask.get(); //接收sum值 注意大小写
System.out.println("总和为"+sum);
}catch(InterruptedException e){
e.printStackTrace();
}catch(ExecutionException e){
e.printStackTrace();
}
}
}
四、线程池
提前创建多个线程,放入线程池中,使用时直接获取,用完放回池中
好处:提高响应速度
降低资源消耗
便于线程管理
corePoolSize:核心池大小
maxmumPoolSize:最大线程数
KeepAliveTime:线程没有任务时最多保持多长时间会终止