创建方式:
目录
//实现遍历100以内的所有偶数
//1.创建一个继承于Thread类的子类
class MyThread extends Thread{
//2.重写Thread类的run()
@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类的子类的对象
MyThread myThread = new MyThread();
MyThread myThread2 = new MyThread();
//4.通过此对象调用start():①:启动当前线程②:调用当前线程的run()
myThread.start();
//问题一:不能直接调用run()方法启动线程
//myThread.run()
//问题二:再启动一个线程,遍历100以内的偶数。不可以让已经start的线程去执行
//myThread.start();
myThread2.start();
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
new Thread(){
@Override
public void run(){
for (int i = 0; i < 100; i++) {
if(i%2!=0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}.start();
}
}
* 1.start():启动当前线程;调用当前线程的run() * 2.run() * 3.currentThread():静态方法,返回当前代码的线程 * 4.getName():获取当前线程的名字 * 5.setName():设置当前线程的名字 * 6.yield() 释放当前cpu的执行权 * 7.join():在线程a中调用线程b的join()方法,此时线程a进入阻塞状态,直到线程b完全执行完以后 、 * 线程a才继续执行 * 8.stop():已过时。当执行此方法时,强制结束当前线程 * 9.sleep(long millitime):当前线程睡眠 * 10.isAlive() :当前线程是否还存活 * * 线程优先级 * 1. * MAX_PRIORITY:10 * MIN_PRIORITY:1 * NORMAL_PRIORITY:5 * 2.如何获取和设置当前线程的优先级 * getPriority() * setPriority(int p)
//1.创建一个实现Runnable接口的类
class MyThreadRunnable implements Runnable {
//2.实现类去实现Runnable中的抽象方法:run()
@Override
public void run(){
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class ThreadRunableTest {
public static void main(String[] args) {
// 3.创建实现类的对象
MyThreadRunnable myThreadRunnable = new MyThreadRunnable();
// 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread thread = new Thread(myThreadRunnable);
// 5.通过Thread类的对象调用start()
thread.start();
//再启动一个线程,遍历100以内的偶数
Thread thread2 = new Thread(myThreadRunnable);
thread2.start();
}
}
方式三:实现Callable接口 ----JDK 5.0 新增
-
//1.创建一个实现Callable的实现类 class NumThread implements Callable<Integer>{ //2.实现call方法,将此线程需要执行的操作声明在call()中 @Override public Integer 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 CallableTest { 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) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?
1.call方法可以有返回值
2.call方法可以抛出异常,被外面的操作捕获,获取异常的信息
3.Callable是支持泛型的
class NumberThread 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 NumberThread1 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 ThreadPool {
public static void main(String[] args) {
//1.提供指定线程数量的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
ThreadPoolExecutor executorService1 = (ThreadPoolExecutor) executorService;
//设置线程池属性
//executorService1.setCorePoolSize(15);
//executorService1.setKeepAliveTime();
//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
NumberThread numberThread = new NumberThread();
NumberThread1 numberThread1 = new NumberThread1();
executorService.execute(numberThread);//适合适用于Runnable
executorService.execute(numberThread1);//适合适用于Runnable
// executorService.submit();//适合适用于Callable
executorService.shutdown();
}
}
通过executorService1设置线程池的属性
线程池主要属性分析:
引用好文:
(51条消息) java自带线程池和队列详细讲解_过天的博客-CSDN博客_线程池队列
线程安全问题
- 使用继承Thread类的方式实现三个窗口卖票
//同步代码块
class Window extends Thread{
private static int ticket = 1000;
private static Object object = new Object();
@Override
public void run(){
while(true) {
//正确的
synchronized(Window.class){ //Class clazz = Window2.class 只加载一次
//synchronized (object) {
// 错误的方式
//synchronized (this){ this 代表window window1 window2 三个对象 锁不唯一
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
//同步方法
class Window extends Thread{
private static int ticket = 1000;
@Override
public void run(){
while(true) {
show();
}
}
private static synchronized void show(){//同步监视器:Window3.class
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}
}
}
public class WindowTest {
public static void main(String[] args) {
Window window = new Window();
Window window1 = new Window();
Window window2 = new Window();
window.setName("窗口1");
window1.setName("窗口2");
window2.setName("窗口3");
window.start();
window1.start();
window2.start();
}
}
- 使用实现Runnable接口的方式实现三个窗口卖票
// 同步代码块
class Window1 implements Runnable{
private int ticket = 1000;
Object object = new Object();
@Override
public void run(){
while(true) {
synchronized(this){
//synchronized(object) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
//同步方法
class Window1 implements Runnable{
private int ticket = 1000;
@Override
public void run() {
while (true) {
show();
}
}
private synchronized void show(){//同步监视器就是this
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}
}
}
public class WindowTest1 {
public static void main(String[] args) {
Window1 w = new Window1();
Thread window = new Thread(w);
Thread window1 = new Thread(w);
Thread window2 = new Thread(w);
window.setName("窗口1");
window1.setName("窗口2");
window2.setName("窗口3");
window.start();
window1.start();
window2.start();
}
}
* 1.问题:卖票过程出现重票和错票 * 2.原因:当某个线程操作车票得过程中,尚未操作完成时,其他线程参与进来,也操作车票 * 3.如何解决:当一个线程在操作ticket的时候,其他线程不能参与进来,直到线程a操作完ticket时,其 * 它线程才能开始操作ticket。这种情况即使线程a出现了阻塞,也不能被改变 * 4.在java中,我们通过同步机制,来解决线程的安全问题 * * 方式一:同步代码块 * synchronized(同步监视器){ * //需要同步的代码 * } * 说明:1.操作共享数据的代码即为需要被同步得代码 * 2.共享数据:多个线程共同操作的变量。比如ticket就是共享数据 * 3.同步监视器:俗称,锁。任何一个类的对象都可以充当锁 * 要求:多个线程必须要共用同一把锁 * * 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器 * 方式二:同步方法 * 如果操作共享数据得代码完整得声明在一个方法中,我们不妨将此方法声明同步方法 * * * 5.同步的方式,解决了线程得安全问题 ---好处 * 操作同步代码时,只能有一个线程参与,其他线程等待。相当于单线程得过程,效率低 ---劣势 * * 比较创建线程的方式比较: * 开发中:优先选择:实现Runnable接口的方式 * 原因:1.实现的方式没有类的单继承性的局限性 * 2.实现的方式更适合来处理多个线程共享数据的情况 * 联系:public class Thread implements Runnable * 相同点:两种方式都需要重写run(),将线程要执行的逻辑声明再run()中
线程通信
* 线程1 与 线程2 交替打印 * 涉及的三个方法 * 1.wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器 * 2.notify():一旦执行此方法,就会唤醒wait的一个线程。如果有多个线程就wait,就唤醒优先级搞得那* 个 * 3.notifyAll():一旦执行此方法,就会唤醒所有被wait的线程 * * 以上三者必须使用在同步代码块和同步方法中 * 三个方法的调用者必须是同步代码块或同步方法的同步监视器 * 定义在Object中 * * 面试题:sleep()和wait()方法的异同 * 相同点:一旦执行方法,都可以使当前的线程进入阻塞状态 * 不同点:1)两个方法声明的位置不同,Thread类中声明sleep(),objec类中声明wait() * 2)调用的要求不同:sleep可以在任何需要的场景下调用。wait()必须使用在同步代码块或同步 * 方法中 * 3)关于是否释放同步监视器:两个方法如果都使用在同步代码块或同步方法中,sleep()不会释 * 放,wait()会释放
class Number implements Runnable{
private int i = 1;
@Override
public void run(){
while(true){
synchronized (this) {
notify();
if(i <= 1000){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ i);
i++;
try {
//使得调用如下wait()方法的线程进入阻塞状态
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread thread = new Thread(number);
Thread thread2 = new Thread(number);
thread.setName("线程一");
thread2.setName("线程二");
thread.start();
thread2.start();
}
}
银行存钱例子
* 两个储户分别向同一个账户存3000元,每次存1000,存三次。每次存完打印余额 * 分析: * 1.是否是多线程问题? 是,两个储户线程 * 2.是否有共享数据?有,账户(或者账户余额) * 3.是否有线程安全问题?有 * 4.需要考虑如何解决线程安全问题?同步机制:有三种
class Account{
private int balance=0;
public int addAccount(int fund){
if(fund > 0) {
balance += fund;
return balance;
}
return balance;
}
}
class Bank implements Runnable {
private Account a = new Account();
private int account = 0;
private ReentrantLock lock = new ReentrantLock(true);
@Override
public void run(){
// synchronized (this){
// for (int i = 0; i < 3; i++) {
// account = a.addAccount(1000);
// System.out.println(Thread.currentThread().getName()+"存款后,账户余额为:"+ account);
//
// }
// }
//
try {
lock.lock();
for (int i = 0; i < 3; i++) {
account = a.addAccount(1000);
System.out.println(Thread.currentThread().getName()+"存款后,账户余额为:"+ account);
}
} finally {
lock.unlock();
}
//错误的方法
// for (int i = 0; i < 3; i++) {
// account = a.addAccount(1000);
// System.out.println(Thread.currentThread().getName()+"存款后,账户余额为:"+ account);
//
// }
}
}
public class BankDemo {
public static void main(String[] args) {
Bank bank = new Bank();
Thread thread = new Thread(bank);
Thread thread1 = new Thread(bank);
Thread thread2 = new Thread(bank);
thread.setName("用户一:");
thread1.setName("用户二:");
thread2.setName("用户三:");
thread.start();
thread1.start();
thread2.start();
}
}