Java线程--扩展

扩展一些Java线程的知识。

首先说一下线程相关的常用类:

1. Timer:定时器、调度器、计时器

2. TimerTask:定时任务。

下面根据这两个类写一个欢迎界面:

import java.awt.BorderLayout;

importjava.awt.CardLayout;

import java.util.Timer;

import java.util.TimerTask;

 

import javax.swing.ImageIcon;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JPanel;

 

import com.sun.awt.AWTUtilities;

 

publicclassLogoextends JFrame{

privateintniIndex = 1;

private JLabel jbShow;

private Timer timer;

public Logo(){

JPanel pnBasic = new JPanel();

//设置布局为东西南北中布局

pnBasic.setLayout(new BorderLayout());

jbShow = new JLabel();

//添加图片,图片的路径可以自己设定

jbShow.setIcon(new ImageIcon("p_w_picpath\\logo\\1.png"));

//把标签添加进来吗,并放在中间的位置

pnBasic.add(jbShow,BorderLayout.CENTER);

 

setContentPane(pnBasic);

//下面两句是设置窗口透明

setUndecorated(true);

AWTUtilities.setWindowOpaque(thisfalse);

//设置标题

setTitle("欢迎界面");

//设置窗口大小

setBounds(200,200,500,400);

//设置窗口自适应

pack();

//设置推出方式

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//设置窗口可见

setVisible(true);

//创建定时器

timer = new Timer();

//安排指定的任务在指定的时间开始进行重复的固定延迟执行

//task - 所要安排的任务

//firstTime - 首次执行任务的时间

//period - 执行各后续任务之间的时间间隔,单位是毫秒

timer.schedule(new LogoMoniter(),1000,500);

}

privateclass LogoMoniter extends TimerTask{

@Override

publicvoid run() {

if(niIndex<7){

niIndex++;

jbShow.setIcon(new ImageIcon("p_w_picpath\\logo\\"+niIndex+".png"));

}else{

//终止此计时器,丢弃所有当前已安排的任务

timer.cancel();

//销毁窗口

dispose();

try {

Thread.sleep(2000);

catch (InterruptedException e) {

e.printStackTrace();

}

//两秒之后再控制台输出hello

System.out.println("hello");

}

}

publicstaticvoid main(String[] args) {

new Logo();

}

}

这是利用定时器做的一个简单的欢迎界面。我们可以自己添加图片,注意把图片的名字命名成数字.的形式,因为我们是根据for循环来获取图片名字的。

 

接下来说一个Java线程中的问题,比如我们去银行存取钱,如果网络存在一点延迟,假设你的亲人就在网络延迟的时候也在存取钱,那么可能最后的余额就会出现问题,下面通过程序来具体看一下:

publicclass Bank1 {

publicstaticvoid main(String[] args) {

Account1 account = new Account1();

Person you1 = new Person(account);

Person youGF1 = new Person(account);

Thread th1 = new Thread(you1);

Thread th2 = new Thread(youGF1);

th1.start();

th2.start();

 

try {

Thread.sleep(4000);// 确保账户操作完毕

catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("最终余额:" + account.balance);

}

}

 

class Account1 {

publicintbalance = 2000;

//取钱

publicvoid withdraw() {

int temp = balance;

temp = temp - 800;

try {

Thread.sleep(1000);

catch (InterruptedException e) {

e.printStackTrace();

}

balance = temp;

System.out.println(Thread.currentThread().getName() + ":余额:"

this.balance);

}

}

 

class Person implements Runnable {

private Account1 account;

public Person(Account1 account) {

super();

this.account = account;

}

@Override

publicvoid run() {

account.withdraw();

}

}

这个程序完成的功能是两个人取同一个账户里的钱。

执行结果:

Thread-0:余额:1200

Thread-1:余额:1200

最终余额:1200

通过结果可以看到,没取之前余额是2000,而两个人每次取的钱都是800,正常情况余额应该是400,这就是我们所说的问题,那么应该如何解决呢?

线程间的同步:本质:把异步转化为同步,把并行转为串行。

有两种方式可以解决

1. 同步语句块:synchronized(obj){}

2. 同步方法:在方法前加synchronized修饰符,拿到的仍然是本类对象的锁。

难点:不容易测出来。

对象锁:每个对象都有一把锁。同步锁、互斥锁。

下面通过程序看一下:

publicclass Bank1 {

publicstaticvoid main(String[] args) {

Account1 account = new Account1();

Person you1 = new Person(account);

Person youGF1 = new Person(account);

Thread th1 = new Thread(you1);

Thread th2 = new Thread(youGF1);

th1.start();

th2.start();

 

try {

Thread.sleep(4000);// 确保账户操作完毕

catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("最终余额:" + account.balance);

}

}

 

class Account1 {

publicintbalance = 2000;

private Object obj = new Object();

 

// 加了一把互斥锁,只能有一个线程执行

public/*synchronized*/void withdraw() {

//两种方法

synchronized (obj) {

int temp = balance;

temp = temp - 800;

try {

Thread.sleep(1000);

catch (InterruptedException e) {

e.printStackTrace();

}

balance = temp;

System.out.println(Thread.currentThread().getName() + ":余额:"

this.balance);

}

}

}

 

class Person implements Runnable {

private Account1 account;

public Person(Account1 account) {

super();

this.account = account;

}

@Override

publicvoid run() {

account.withdraw();

}

}

执行结果:

Thread-0:余额:1200

Thread-1:余额:400

最终余额:400

通过上面的两种方式,我们可以看到余额正常了。