一. 多线程的创建
1. 方式一:继承Thread类
(1)创建一个继承于Thread类的子类;
(2)重写Thread类的run():将此线程要执行的操作声明在run()中;
(3)创建子类对象;
(4)通过此对象调用start():启动当前线程,调用当前线程的run();
注意:不能直接调用run()来启动线程;如果想再启动一个线程来执行该任务,需要再创建一个对象来调用start();如果想同时启动多个不同任务的线程,需要创建多个任务的子类,再分别创建对应的对象来调用start()。
public class Test{
public static void main(String[] args) {
MyThread1 thread = new MyThread1();
thread.start();
MyThread1 thread1 = new MyThread1();
thread1.start();
MyThread2 thread2 = new MyThread2();
thread2.start();
}
}
class MyThread1 extends Thread{
@Override
public void run() {
for(int i = 0; i <= 100; i++){
if(i % 2 == 0){
System.out.println("偶数" + i);
}
}
}
}
class MyThread2 extends Thread{
@Override
public void run() {
for(int i = 0; i <= 100; i++){
if(i % 2 != 0){
System.out.println("奇数" + i);
}
}
}
}
//使用匿名子类方式启动多线程
public class Test{
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for(int i = 0; i <= 100; i++){
if(i % 2 == 0){
System.out.println("偶数" + i);
}
}
}
}.start();
new Thread(){
@Override
public void run() {
for(int i = 0; i <= 100; i++){
if(i % 2 != 0){
System.out.println("奇数" + i);
}
}
}
}.start();
}
}
2. 方式二:实现Runnable接口(通常优先考虑使用这种方式)
(1)创建一个实现了Runnable接口的类;
(2)实现类去重写Runnable中的抽象方法:run();
(3)创建实现类的对象;
(4)创建Thread类的对象,将实现类的对象作为参数传递到Thread类的构造器中;
(5)通过Thread类的对象调用start()。
public class Test{
public static void main(String[] args) {
MyThread mythread = new MyThread(); //3:创建实现类的对象
Thread thread = new Thread(mythread); //4:创建Thread类的对象,将实现类的对象作为参数传递到Thread类的构造器中
thread.start(); //5:通过Thread类的对象调用start()
Thread thread2 = new Thread(mythread); //启动另一个线程
thread2.start();
}
}
class MyThread implements Runnable{ //1:创建一个实现了Runnable接口的类
@Override
public void run() { //2:实现类去重写Runnable中的抽象方法
for(int i = 0; i <= 100; i++){
if(i % 2 == 0){
System.out.println(i);
}
}
}
}
//使用匿名子类方式启动多线程
public class Test{
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0; i <= 100; i++){
if(i % 2 == 0){
System.out.println("偶数" + i);
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0; i <= 100; i++){
if(i % 2 != 0){
System.out.println("奇数" + i);
}
}
}
}).start();
}
}
3. Thread类中的方法:
(1)start():启动当前线程,调用当前线程的run();
(2)run():通常需要重写此方法,将该线程要执行的操作声明在此方法中;
(3)currentThread():静态方法,返回执行当前代码的线程;
(4)getName():获取当前线程的名字;
(5)setName():设置当前线程的名字;
public class Test{
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.setName("线程1");
thread.start();
Thread.currentThread().setName("主线程");
for(int i = 0; i <= 100; i++){
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
class MyThread extends Thread{
@Override
public void run() {
for(int i = 0; i <= 100; i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
(6)yield():释放当前线程CPU的执行权;
(7)join():在线程A中调用线程B的join(),此时线程A进入阻塞状态,直到线程B全部执行结束后,线程A才解除阻塞状态;
(8)stop():强制结束当前线程;
(9)sleep():让当前线程 “睡眠” 指定时间,在睡眠期间,该线程是阻塞状态;
(10)isalive():判断当前线程是否存活。
4. 线程的优先级:
优先级分为:MAX_PRIORITY:10 MIN_PRIORITY:1 NORM_PRIORITY:5
获取线程的优先级:getPriority()
设置线程的优先级:setPriority(int p)
注意:高优先级的线程要抢占低优先级线程CPU的执行权,高优先级的线程以高概率优先执行,但是不意味着只有高优先级的线程全部执行完之后,低优先级的线程才执行。
public class Test{
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.setName("线程1");
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
Thread.currentThread().setName("主线程");
for(int i = 0; i <= 100; i++){
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority());
}
}
}
}
class MyThread extends Thread{
@Override
public void run() {
for(int i = 0; i <= 100; i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority());
}
}
}
}
二. 线程同步
例:卖票 —— 创建三个售票窗口,总票数为100张
public class Test{
public static void main(String[] args) {
Window window1 = new Window();
Window window2 = new Window();
Window window3 = new Window();
window1.setName("窗口一");
window2.setName("窗口二");
window3.setName("窗口三");
window1.start();
window2.start();
window3.start();
}
}
class Window extends Thread{
private static int ticket = 100;
@Override
public void run() {
while(true){
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
public class Test{
public static void main(String[] args) {
Window window = new Window();
Thread thread1 = new Thread(window);
Thread thread2 = new Thread(window);
Thread thread3 = new Thread(window);
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
class Window implements Runnable{
private int ticket = 100;
@Override
public void run() {
while(true){
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
注意:上述例子多个线程共同操作同一个数据,会存在线程的安全问题
解决方法:当线程A在操作ticket时,其他线程不能参与进来,直到线程A操作完ticket时(即使线程A出现了阻塞),其他线程才可以操作ticket。
1. 同步代码块
synchronized(同步监视器){
多个线程需要同步的代码
}
说明:(1)同步的代码是指操作共享数据的代码,共享的数据是指多个线程共同操作的变量;
(2)任何一个类的对象都可以充当同步监视器(锁),所有线程必须共用一把锁。
注意:该方法在操作同步代码块时,实际上是单线程,只有一个线程参与,其他线程等待。
public class Test{
public static void main(String[] args) {
Window window = new Window();
Thread thread1 = new Thread(window);
Thread thread2 = new Thread(window);
Thread thread3 = new Thread(window);
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
class Window implements Runnable{
private int ticket = 100;
Object object = new Object();
@Override
public void run() {
while(true){
synchronized(object){
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class Test{
public static void main(String[] args) {
Window window = new Window();
Thread thread1 = new Thread(window);
Thread thread2 = new Thread(window);
Thread thread3 = new Thread(window);
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
class Window implements Runnable{
private int ticket = 100;
@Override
public void run() {
while(true){
synchronized(this){ //this指代创建的唯一对象window
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class Test{
public static void main(String[] args) {
Window window1 = new Window();
Window window2 = new Window();
Window window3 = new Window();
window1.setName("窗口一");
window2.setName("窗口二");
window3.setName("窗口三");
window1.start();
window2.start();
window3.start();
}
}
class Window extends Thread{
private static int ticket = 100;
static Object object = new Object();
@Override
public void run() {
while(true){
//synchronized(this){ 错误,这里的this指代三个对象
synchronized(object){
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class Test{
public static void main(String[] args) {
Window window1 = new Window();
Window window2 = new Window();
Window window3 = new Window();
window1.setName("窗口一");
window2.setName("窗口二");
window3.setName("窗口三");
window1.start();
window2.start();
window3.start();
}
}
class Window extends Thread{
private static int ticket = 100;
@Override
public void run() {
while(true){
//synchronized(this){ 错误,这里的this指代三个对象
synchronized(Window.class){
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
}
2.同步方法
同步方法仍然涉及到锁,只是不用显示的声明,非静态的同步方法中锁是this,静态的同步方法中锁是当前类本身。
public class Test{
public static void main(String[] args) {
Window window = new Window();
Thread thread1 = new Thread(window);
Thread thread2 = new Thread(window);
Thread thread3 = new Thread(window);
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
class Window implements Runnable{
private int ticket = 100;
@Override
public void run() {
while(true){
show();
}
}
public synchronized void show(){
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
ticket--;
}
}
}
public class Test{
public static void main(String[] args) {
Window window1 = new Window();
Window window2 = new Window();
Window window3 = new Window();
window1.setName("窗口一");
window2.setName("窗口二");
window3.setName("窗口三");
window1.start();
window2.start();
window3.start();
}
}
class Window extends Thread{
private static int ticket = 100;
static Object object = new Object();
@Override
public void run() {
while(true){
show();
}
}
//错误:public synchronized void show(){
public static synchronized void show(){
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
ticket--;
}
}
}
3.使用Lock锁
import java.util.concurrent.locks.ReentrantLock;
public class Test{
public static void main(String[] args) {
Window window = new Window();
Thread thread1 = new Thread(window);
Thread thread2 = new Thread(window);
Thread thread3 = new Thread(window);
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
class Window implements Runnable{
private int ticket = 100;
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try{
lock.lock();
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + ",票号为:" + ticket);
ticket--;
}else{
break;
}
}finally {
lock.unlock();
}
}
}
}
synchronized与Lock的异同:(1)相同:都用来解决线程安全问题;(2)不同:synchronized在执行完同步代码之后自动解开锁,Lock需要手动启动同步(lock()),结束同步时需要手动解锁(unlock())。
三. 线程通信
例子:使用两个线程交替打印1-100
public class Test{
public static void main(String[] args) {
Printer printer = new Printer();
Thread thread1 = new Thread(printer);
Thread thread2 = new Thread(printer);
thread1.setName("打印一");
thread2.setName("打印二");
thread1.start();
thread2.start();
}
}
class Printer implements Runnable{
private int number = 1;
@Override
public void run() {
while(true){
synchronized (this){
notify(); //唤醒被阻塞的线程 省略的是this.
if(number <= 100){
System.out.println(Thread.currentThread().getName() + "," + number);
number++;
try{
wait(); //调用wait方法使线程进入阻塞状态 省略的是this.
}catch (InterruptedException e){
e.printStackTrace();
}
}else{
break;
}
}
}
}
}
wait():一旦执行此方法,当前线程进入阻塞状态,释放锁
notify():一旦执行此方法,就会唤醒被wait()的一个线程,如果多个线程被wait(),就唤醒优先级高的那个。
notify():一旦执行此方法,就会唤醒所有被wait()的线程。
注意:wait()、notify()、notify()只能使用在同步代码块或同步方法中,且只能被同步监视器调用。
sleep与wait的不同:(1)sleep()声明在Thread类中,wait()声明在Object类中;(2)sleep()可以在任何情况下调用,wait()只能使用在同步代码块或同步方法中;(3)如果两个方法都使用在同步代码块或同步方法,sleep()不会释放同步监视器,wait()会释放同步监视器。
四. 创建多线程的两种新方式
1. 新方式一:实现Callable接口
(1)创建一个实现了Callable接口的类;
(2)实现类去重写Callable中的call()方法,将此线程需要执行的操作声明在call()中,此方法可以有返回值;
(3)创建Callable接口实现类的对象;
(4)创建FutureTask类的对象,将实现类的对象作为参数传递到FutureTask类的构造器中;
(5)创建Thread类的对象,将FutureTask类的对象作为参数传递到Thread类的构造器中;
(6)通过Thread类的对象调用start()。
(7)可以使用get()方法获取Callable中call()方法的返回值
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test{
public static void main(String[] args) {
MyThread mythread = new MyThread();
FutureTask futureTask = new FutureTask(mythread);
Thread thread = new Thread(futureTask);
thread.start();
try{
Object sum = futureTask.get();
System.out.println("总和:" + sum);
}catch(InterruptedException e){
e.printStackTrace();
}catch(ExecutionException e){
e.printStackTrace();
}
}
}
class MyThread implements Callable {
@Override
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;
}
}
2. 新方式二:使用线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test{
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10); //创建一个固定线程数量的线程池
MyThread1 mythread1 = new MyThread1();
executorService.execute(mythread1); //用于实现Runnable接口
//executorService.submit(); 用于实现Callable接口
MyThread2 mythread2 = new MyThread2();
executorService.execute(mythread2);
executorService.shutdown(); //关闭线程池
}
}
class MyThread1 implements Runnable {
@Override
public void run(){
for(int i = 0; i <= 100; i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + "," + i);
}
}
}
}
class MyThread2 implements Runnable {
@Override
public void run(){
for(int i = 0; i <= 100; i++){
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + "," + i);
}
}
}
}