多线程的四种创建方式
1.继承Thread类
//创建一个继承于Thread类的子类
public class MThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2==0)
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class Threadtest {
public static void main(String[] args) {
//3.创建Thread类的子类的对象
MThread t1=new MThread();
//4.通过此对象调用start()
t1.start(); //start方法来实现启动线程,并调用run方法,从而真正实现了多线程。同时run方法它只是一个普通的函数方法,不需要线程调用start方法也可以调用它.
}
}
2.实现Runnable接口
class window implements Runnable{
private int ticket =100;
public void run() {
while(true) {
if(ticket >0) {
System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+ticket);
ticket--;
}else
break;
}
}
}
public class RunnableTest {
public static void main(String[] args) {
window w=new window();
//三个线程用的同一个window,都执行同一个window的run,因此在设置票的数量时不需要设置为static
Thread t1=new Thread(w);
Thread t2=new Thread(w);
Thread t3=new Thread(w);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
3.实现Callable接口
//1.创建一个实现Callable的实现类
class NumThread implements Callable{
//2.实现call方法,将此线程需要执行的操作声明在call()方法中
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 CallableWay {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
NumThread numThread =new NumThread();
//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask对象
FutureTask futureTask =new FutureTask(numThread);
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()方法
new Thread(futureTask).start();
try {
//6.获取Callable中call()的返回值
//get()返回值即为FutureTask构造参数Callable实现类重写的call()的返回值
Object sum = futureTask.get();
System.out.println("总和为:"+sum);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4.使用线程池
class NumberThread implements Runnable{
public void run() {
for(int i=0;i<=100;i++) {
if(i%2==0) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class Thread4 {
public static void main(String[] args) {
//1.提供指定线程数量的线程池
ExecutorService service =Executors.newScheduledThreadPool(10);
//2.执行指定的线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread()); //适用于Runnable
//service.submit(); //适用于Callable
//3.关闭线程池
service.shutdown();
}
}
线程的优先级
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5 —>默认优先级
如何获取和设置当前线程优先级:
getpriority() //获取线程的优先级
setpriority(int p) //设置线程的优先级
说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上来讲,高优先级的线程高概率的情况下被执行,并不意味着只有高优先级的线程执行完后,低优先级的线程才执行。
测试Thread中常用的方法
1.star():启动当前线程;调用当前线程的run()
2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3.currentThread():静态方法,返回执行当前代码的线程
4.getName():获取当前线程的名字
5.setName():设置当前线程的名字
6.yield():释放当前线程cpu的执行权,各个线程重新“竞争”7.join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完之后,线程a才结束阻塞状态
8.stop():已过时。当执行此方法时,强制结束当前线程
9.sleep(long millitime):让当前线程“睡眠”指定millitime毫秒。在指定的时间内,当前线程是处于阻塞状态
10.isAlive():判断当前线程是否存活
线程的生命周期
多线程的同步控制
1.同步代码块
synchronized(同步监视器){
// 需要被同步的代码
}
下面展示实现Runnable接口的情况。
class TicketWindow implements Runnable {
private int ticket = 100;
private Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖出第 " + ticket + " 张票");
ticket--;
} else {
break;
}
}
}
}
}
public class ThreadSync {
public static void main(String[] args) {
TicketWindow ticketWindow = new TicketWindow();
Thread thread1 = new Thread(ticketWindow);
Thread thread2 = new Thread(ticketWindow);
Thread thread3 = new Thread(ticketWindow);
thread1.setName("售票窗口1");
thread2.setName("售票窗口2");
thread3.setName("售票窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
下面展示继承Thread类的情况。
class TicketWindow1 extends Thread { private static int ticket = 100; private static Object obj = new Object(); @Override public void run() { while (true) { synchronized (obj) { if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 卖出第 " + ticket + " 张票"); ticket--; } else { break; } } } }}public class ThreadSync1 { public static void main(String[] args) { Thread thread1 = new TicketWindow1(); Thread thread2 = new TicketWindow1(); Thread thread3 = new TicketWindow1(); thread1.setName("售票窗口1"); thread2.setName("售票窗口2"); thread3.setName("售票窗口3"); thread1.start(); thread2.start(); thread3.start(); }}class TicketWindow1 extends Thread {
private static int ticket = 100;
private static Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖出第 " + ticket + " 张票");
ticket--;
} else {
break;
}
}
}
}
}
public class ThreadSync1 {
public static void main(String[] args) {
Thread thread1 = new TicketWindow1();
Thread thread2 = new TicketWindow1();
Thread thread3 = new TicketWindow1();
thread1.setName("售票窗口1");
thread2.setName("售票窗口2");
thread3.setName("售票窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
对比二者不同二者不同在于synchronized(同步监视器)中的同步监视器,
实现Runnable接口的情况下,同步监视器不需要用static,因为Runnable接口的实现类只被创建一次,三个线程的同步监视器是同一个。而继承Thread类的情况下,同步监视器如不声明为static则被声明了三次,三个线程的同步监视器不是同一。
2.同步方法
访问修饰符 synchronized 返回值 方法名(参数列表) {
// 同步代码块
}
下面展示实现Runnable接口的情况。
class TicketWindow implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (ticket > 0) {
sellTicket();
}
}
public synchronized void sellTicket() {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖出第 " + ticket + " 张票");
ticket--;
}
}
}
下面展示继承Thread类的情况。
class TicketWindow1 extends Thread {
private static int ticket = 100;
@Override
public void run() {
while (ticket > 0) {
sellTicket();
}
}
public static synchronized void sellTicket() {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖出第 " + ticket + " 张票");
ticket--;
}
}
}
3.同步锁
// 1 获得一个锁
Lock lock = new ReentrantLock();
// 2 加锁
lock.lock();
// 同步代码块
// 3 解锁
lock.unlock();
下面展示代码实现。
class TicketWindowLock implements Runnable {
private int ticket = 100;
// 1 获得一个锁
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
// 2 上锁
lock.lock();
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖出第 " + ticket + " 张票");
ticket--;
} else {
break;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 3 解锁。使用 try catch finally,将解锁操作放在finally语句块中,保证锁一定会被释放
lock.unlock();
}
}
}
}
synchronized与lock锁有何异同
- 相同:二者都是用来解决线程安全问题
- 不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器,lock需要手动的打开释放。
线程通信
wait/notify模式
涉及到的三个方法
1.wait()
:一旦执行此方法,当前线程就会进入阻塞状态,并且释放同步监视器。
2.notify()
:一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就优先唤醒高优先级的线程
3.notifyAll()
:一旦执行此方法,所有的线程都会被唤醒
说明上述三个方法都必须使用在同步代码块或同步方法中的同步监视器中,否则会出现异常
上述三个方法都是定义在Object类中的。
sleep和wait的异同
相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
不同点:
1)俩个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()
2)调用的要求不同:sleep可以在任何场景下调用,而wait只能在同步代码块或者同步方法的同步监视器中
3)关于是否释放同步监视器:如果俩个方法都使用在同步代码块或同步方法中,sleep()不释放,而wait()释放。
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!